Informatik I: Programmierung

Werbung
1
Informatik I: Programmierung
Herbert Kuchen
V4 + Ü2, WWU Münster, WS 2002/2003
Mo+Do 14.15-15.45 Uhr
Gliederung:
1. Einführung
2. Objektorientierte Programmierung (in Java)
3. Deklarative Programmierung (in Haskell)
4. Semantik von Programmiersprachen
2
Literatur
• Java:
? J. Bishop: Java lernen, 752 S. + CD, Addison Wesley, 2001.
? D.D. Riley: The Object of Java – Introduction to Programming Using Software
Engineering Principles, 670 S. + CD, Addison Wesley, 2002.
? D. Arnow, G. Weiss: Introduction to Programming Using Java, 805 S., Addison
Wesley, 2000.
• Haskell:
? S. Thompson: Haskell – The Craft of Functional Programming, AddisonWesley, 1999.
? R. Bird: Introduction to Functional Programming using Haskell, Prentice Hall,
1998.
• Semantik:
? G. Winskel: The Formal Semantics of Programming Languages, MIT Press,
1993.
3
1. Einführung
Programmiersprache: “zur Festlegung der Arbeitsabläufe eines Rechners”
Klassifikation nach Programmierniveau
1) Maschinensprachen (1. Generation)
2) Assemblersprachen (2. Generation)
3) Höhere Programmiersprachen (3. Generation)
4) 4GL-Sprachen (anwendungsbezogen)
4
von-Neumann-Rechnermodell
Inhalt
Adresse
Programmzähler
0
27
42
1
03
01
2
18
42
...
...
02
...
...
...
00
Programm
Speicher
42
...
n-1
n
laden
CPU
speichern
Akku/Register
verarbeiten
Daten
5
Maschinensprachen
Programm: Folge von elementaren Befehlen bestehend
aus Operationscode und Argument(en)
Befehle für:
• Transport zwischen Speicher und CPU
(Laden, Speichern, . . . )
• arithmetische und logische Befehle
(Addition, Konjunktion, . . . ) auf CPU-Registern
• Sprungbefehle (bedingt/unbedingt, absolut/relativ)
• ...
6
Beispiel:
27
|{z}
inAkku
laden
42
|{z}
Inhalt von
Adresse 42
03
|{z}
addiere zu
Akku − Inhalt
01
|{z}
Zahl 1
18
|{z}
speichere
Akku − Inhalt
42
...
|{z}
Adresse 42
beachte: verschiedene “Adressierungsarten” (abh. von Op.-Code)
Eigenschaften:
• sehr niedriges Programmierniveau
• aufwendig, fehleranfällig, schwer modifizierbar
• rechnerabhängig, nicht portierbar
• in Praxis zur Programmierung nicht verwendet
7
Assemblersprachen
• wie Maschinensprache, jedoch symbolische Namen
für Op-Codes, Speicherplätze, Sprungmarken, . . .
Beispiel:
DEFINE x 42
LOAD x
ADD 1
STORE x
Eigenschaften:
• sehr niedriges Programmierniveau
• aufwendig, fehleranfällig, rechnerabhängig
• leicht in Maschinensprache übersetzbar
(nur die “versteht” der Rechner; → Assembler)
• nur in Ausnahmefällen zur Programmierung verwendet
8
Höhere Programmiersprachen
• heute in Praxis üblich
Klassifikation nach Paradigmen
a) imperative Programmiersprachen
z.B. Pascal, C, Fortran, Modula, Ada, Basic, Algol, Cobol, PL/I, . . .
⊃ objektorientierte PS:
z.B. Java, C++, C#, Eiffel, Smalltalk, Ada95, . . .
? Grundidee: Programm bewirkt Speichertransformation
(→ von-Neumann-Modell)
? oft effizienter
Bsp.: x := x + 1
9
b) deklarative Programmiersprachen
% funktionale PS: z.B. Haskell, Miranda, ML, Lisp, Scheme, Erlang, . . .
& logische PS: z.B. Prolog
? Grundidee: ausführbare Spezifikation, “was statt wie”
? höheres Programmierniveau, z.T. weniger effizient
10
Ziele der Programmierung
• Korrektheit, Robustheit
• leichte Wartbarkeit, Verständlichkeit, Testbarkeit
durch geeignete Programmierkonzepte, Modularisierung, Strukturierung,
geeignetes Layout, sprechende Namen (z.B. Variable “Gehalt” statt “x”),
Kommentare,. . .
• effiziente Programme
durch geignete Algorithmen und Datenstrukturen (→ Informatik II)
11
2. Objektorientierte Programmierung in Java
2.1 Erste Java-Programme
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World!");}
}
Schritte zur Ausführung:
• J2SE <http://java.sun.com/j2se/1.4.1/download.html>
(oder andere Entwicklungsumgebung) besorgen und installieren (u.a. Pfade!)
• Klasse mit Editor erstellen und in Datei HelloWorld.java speichern
• Compilieren: javac HelloWorld.java erzeugt HelloWorld.class
(Byte-Code, Zwischencode)
• Ausführen: java HelloWorld führt die Methode main der Hauptklasse aus
12
Grundbegriffe der Objektorientierung
• ein Java-Programm besteht aus mehreren Klassen
• zu jeder Klasse gibt es Objekte (Instanzen)
• alle Instanzen der gleichen Klasse haben gleiche Struktur (Attribute, Variablen)
und gleiches Verhalten (Methoden)
• jedem Attribut ist ein Wertebereich (wie z.B. ganze Zahl (int), “reelle Zahl”
(float) oder Verweis auf ein Nachbarobjekt) zugeordnet
• jedes Attribut kann nur einen Wert des zugehörigen Wertebereichs annehmen
• ein Objekt hat eine Identität (OID)
(Mehrfachverweise möglich; Objekte mit gleichem Zustand ggfs. ungleich)
13
Klassendiagramme
• Klassen und deren Beziehungen werden durch Klassendiagramme visualisiert
• Klasse: dargestellt durch Rechteck (unterteilt in Name, Attribute und Methoden)
• eine (Nachbarschaft-)Beziehung (Assoziation) zwischen zwei Klassen wird
dargestellt durch Linie zwischen den zugehörigen Rechtecken
Beispiel:
SimpelBank
Multiplizitäten
1
ueberweise
main
Klasse
Konto
2 nr
saldo
getSaldo
Assoziation
setSaldo
Name
Attribute
Methoden
14
2.1.1 Beispiel: SimpelBank in Java
public class SimpelBank{
// Attribute (inkl. Assoziationen)
private static Konto kunde1 = new Konto(1,50);
private static Konto kunde2 = new Konto(2,50);
// Methoden
public static void ueberweise(Konto k1, Konto k2,int betrag){
k1.setSaldo(k1.getSaldo()-betrag);
//Methodenaufrufe
k2.setSaldo(k2.getSaldo()+betrag);}
public static void main(String[] args){
System.out.println("alter Saldo von Kunde
System.out.println("alter Saldo von Kunde
ueberweise(kunde1,kunde2,10);
System.out.println("neuer Saldo von Kunde
System.out.println("neuer Saldo von Kunde
}
1: "+kunde1.getSaldo());
2: "+kunde2.getSaldo());
1: "+kunde1.getSaldo());
2: "+kunde2.getSaldo());}
15
Beispiel: SimpelBank in Java (Fortsetzung)
public class Konto{
// Attribute
private int nr;
private int saldo;
//Assoziation zu SimpelBank-Obj. hier weggelassen
// Methoden, hier: Kontruktor, getter und setter
public Konto(int nr, int betrag){
this.nr = nr;
// Zuweisung
saldo = betrag;}
public int getSaldo(){
return saldo;}
// Rueckgabeanweisung
public void setSaldo(int betrag){
saldo = betrag;}
}
16
Syntax und Semantik
• jedes Sprachkonstrukt hat eine Syntax und eine Semantik
• Syntax: Wie sieht das Konstrukt aus?
• Semantik: Was bedeutet es?
• die Syntax wird oft beschrieben durch (endlich viele) Regeln in
erweiterter Backus-Naur-Form (EBNF) (s.u.)
• Beschreibung der Semantik:
? präzise mathematisch (vgl. Kapitel 4) oder
? weniger präzise in natürlicher Sprache (hier: Deutsch)
17
Erweiterte Backus-Naur-Form
• EBNF-Regel hat Gestalt (Syntax): <Nichtterminal> ::= <EBNF-Ausdruck>
wobei ein <EBNF-Ausdruck> wie folgt aufgebaut sein kann:
(metasprachliche Erläuterungen selbst in EBNF)
<EBNF-Ausdruck>
<EBNF-Ausdruck>
<EBNF-Ausdruck>
<EBNF-Ausdruck>
<EBNF-Ausdruck>
<EBNF-Ausdruck>
<EBNF-Ausdruck>
::=
::=
::=
::=
::=
::=
::=
<Nichtterminal>
<Terminal>
<EBNF-Ausdruck>∗
<EBNF-Ausdruck>?
<EBNF-Ausdruck>+
<EBNF-Ausdruck> |
<EBNF-Ausdruck>
(<EBNF-Ausdruck>)
// beliebig häufige Wiederholung (auch 0)
// optionaler Ausdruck (0 oder 1 Mal)
// mindestens einmalige Wiederholung
// Alternative
// Klammern klären Zusammengehörigkeit
• zu jedem Nichtterminal: weitere EBNF-Regeln, die den syntaktischen Aufbau
des hierdurch beschriebenen Konstrukts festlegen
• Terminale werden nicht durch weitere Regeln beschrieben, sondern so verwendet,
“wie sie in dem EBNF-Ausdruck vorkommen”
18
Klassendeklaration
• im Beispiel: SimpelBank und Konto
• allgemeine Syntax (im folgenden generell vereinfacht):
<Klasse> ::= <Sichtbarkeit> class <Klassenname>{
<Attribut-Deklaration>∗
<Methode>∗}
• Semantik: legt Struktur (Attribute) und Verhalten (Methoden) jedes Objekts
der Klasse fest
19
Attribut-Deklaration
• Beispiel: private int saldo;
• Syntax: <Attribut-Deklaration> ::=
<Sichtbarkeit> <Typ> <Attributname> (= <Ausdruck>)? ;
• Semantik:
? reserviert benannten Speicherplatz in jedem Objekt der zugehörigen Klasse
? dieser Speicherplatz kann nur Werte des angegebenen Typs aufnehmen
? Initialisierung optional
20
Sichtbarkeit und Namen
<Sichtbarkeit> ::= private | public | . . .
• Sichtbarkeitsangaben regeln die Zugriffsrechte auf eine Variable bzw. Methode
• private: nur Methoden der betrachteten Klasse dürfen zugreifen
• public: von überall her darf zugegriffen werden
(bei Attributen vermeiden!)
<Klassenname>, <Attributname>, <Methodenname>, sonstige Namen:
Buchstabe gefolgt von Buchstaben und Ziffern
21
Typen
<Typ> ::= int | float | <Klassenname> | . . .
• Speicherplätze enthalten Bitfolgen
• die gleiche Bitfolge kann (semantisch) unterschiedlich interpretiert werden
• z.B.: 2.0 (float) und 1073741824 (int) durch gleiche Bitfolge repräsentiert
• der Typ legt fest, wie die Bitfolge zu interpretieren ist
22
Methodendeklarationen
• Beispiel: public int getSaldo(){return saldo;}
• Syntax: <Methode> ::=
<Sichtbarkeit>(<Typ>| void) Methodenname (<Parameterliste>)
{ <Anweisung>∗ }
• Semantik:
? stellt für die Objekte der Klasse eine benannte,
parametrisierte Anweisungsfolge zur Verfügung
? durch Methodenaufruf (s.u.): Anweisungsfolge wird ausgeführt
? das Ergebnis ist vom angegebenen Typ (oder fehlt: void)
wobei
<Parameterliste> ::= (<Parameter> (, <Parameter>)∗)?
<Parameter> ::= <Typ> <Parametername>
23
Anweisungen
• Syntax: <Anweisung> ::=
<Zuweisung> | <Rückgabeanweisung> | <Ausdruck>? ; |
{ <Anweisung>∗ } | . . .
• Semantik:
? s. Zuweisung, Rückgabeanweisung, . . .
? wird ein Ausdruck als Anweisung verwendet, so interessieren nur die bei seiner
Auswertung auftretenden Seiteneffekte (z.B. Ein-/Ausgaben);
der berechnete Wert wird ignoriert
? Anweisungsfolgen werden von links nach rechts (oben nach unten) ausgeführt
24
Zuweisungen
• Beispiele:
? saldo = betrag;
? saldo = saldo + 1;
• Syntax: <Zuweisung> ::= <Attributname> = <Ausdruck>;
• Semantik:
? der Ausdruck wird ausgerechnet,
? der erhaltene Wert wird in dem mit <Attributname> benannten Speicherplatz
des betrachteten Objekts abgelegt
? Achtung: Zuweisung nicht verwechseln mit math. Gleichung!
25
Rückgabeanweisungen
• Beispiel: return saldo;
• Syntax: <Rückgabeanweisung> ::= return <Ausdruck>?;
• Semantik:
?
?
?
?
Rückgabeanweisungen kommen nur in der Anweisungsfolge einer Methode vor
der Ausdruck wird ausgerechnet und
als Ergebnis des Aufrufs der zugehörigen Methode geliefert
der Typ dieses Werts muss dem aus der zugehörigen Methodendeklaration
entsprechen
26
Ausdrücke
• Beispiele:
? Konstanten, z.B. 2 oder 2.0
? i+j
? kunde1.getSaldo()
• Syntax: <Ausdruck> ::= <Konstante> | <Variable> | <Methodenaufruf> |
<Ausdruck> <Operationssysmbol> <Ausdruck>
• Semantik:
? der Wert einer numerischen Konstanten ist “die Konstante selber”
? der Wert einer Variablen (inkl. Attribut und Parameter) findet sich
im zugehörigen Speicherplatz
? bei zusammengesetzten Ausdrücken:
∗ die Teilausdrücke werden von links nach rechts ausgewertet
∗ die so erhaltenen Werte werden gemäß des Operationssymbols verknüpft
∗ Beispiele für Operationssymbole: +, -, *, /, <<, !=, &&
27
Methodenaufrufe
• Beispiele: kunde1.getSaldo()
kunde1.setSaldo(10)
• Syntax: <Methodenaufruf> ::=
(<Ausdruck>.)? <Methodenname>( (<Ausdruck> (, <Ausdruck>)∗)?)
• Semantik: (call by value)
? der Ausdruck vor . wird ausgewertet und muss eine Objektreferenz liefern
? die übrigen Ausdrücke werden ausgewertet
? ihre Werte werden den Parameter-Variablen zugeordnet,
die in der zugehörigen Methodendeklaration vorkommen
? die zur Methode gehörige Anweisungsfolge wird ausgeführt
? hierbei wird auf die Attribute des ermittelten Objekts zugegriffen
? Abarbeitung endet i.d.R. bei Erreichen einer Rückgabeanweisung
? Wert eines Methodenaufrufs: in Rückgabeanweisung angegebener Wert
28
Beispiel: Methodenaufruf mit call-by-value-Semantik
public class Testcbv{
private static int k = 3;
public static void foo(int i, int j){
System.out.print("i: "+ i);
i = i+j;
System.out.print(", i: "+ i);}
public static void main(String argv[]){
foo(k,k+2);
System.out.println(", k: "+ k);}
}
liefert: i: 3, i: 8, k: 3
29
Konstruktoren
• ein Konstruktor ist eine spezielle Methode ohne Ergebnistyp (auch nicht void)
• jeder Konstruktor einer Klasse heißt genau wie die Klasse
• der Ausdruck: new <Konstruktor>((<Ausdruck> (, <Ausdruck>)∗)?)
liefert ein neues Objekt der zugehörigen Klasse
• Beispiel: new Konto(1,50)
• beim Anlegen dieses Objekts werden die Anweisungen
des Konstruktors ausgeführt
• zu jeder Klasse kann es mehrere Konstruktoren
mit unterschiedlichen Parametertypen geben
• hat eine Klasse keinen Konstruktor, so wird ein trivialer Konstruktor ohne
Parameter automatisch ergänzt
30
2.1.2 Beispiel: Verwandschaft
public class Person{
// Attribute (inkl. Assoziationen)
private String name;
private Person vater;
private Person mutter;
// Verknüfung mit Kindern ignoriert
// Methoden
public Person(String n, Person v, Person m){
name = n; vater = v; mutter = m;}
public String getName(){return name;}
public Person getVater(){return vater;}
public Person getMutter(){return mutter;}
...
2
Person
name
getName
getVater
getMutter
grosseltern
main
*
31
public void grosseltern(){
if (vater != null){
if (vater.getMutter() != null)
System.out.println(vater.getMutter().getName());
if (vater.getVater() != null)
System.out.println(vater.getVater().getName());}
if (mutter != null){
if (mutter.getMutter() != null)
System.out.println(mutter.getMutter().getName());
if (mutter.getVater() != null)
System.out.println(mutter.getVater().getName());}}
public static void main(String[] args){
Person adam = new Person("Adam",null,null);
Person eva = new Person("Eva",null,null);
Person seth = new Person("Seth",adam,eva);
Person lamech = new Person("Lamech",seth,null);
Person noah = new Person("Noah",lamech,null);
seth.grosseltern(); lamech.grosseltern(); noah.grosseltern();}
}
32
neue Konstrukte im Verwandschaft-Beispiel:
• bedingte Anweisung (if)
• Typen boolean und String
• lokale Variablen
33
Weitere Typen
<Typ> ::= . . . | boolean | String
• der Typ boolean umfasst die Wahrheitswerte true und false
• String ist kein Basistyp (wie z.B. int und boolean) sondern eine Klasse
• eine String-Objekt repräsentiert eine Zeichenkette
• Stringkonstanten (Literale) werden in Anführungszeichen eingeschlossen
• Strings können durch + verkettet werden
• Beispiel: "Dies ist ein " + "verketteter String"
34
Bedingte Anweisung (Verzweigung)
• Beispiele:
? if (vater != null){...}
? if (x > y) max = x; else max := y;
• Syntax: <Anweisung> ::= <if-Anweisung> | . . .
<if-Anweisung> ::= if ( <Ausdruck> ) <Anweisung> (else <Anweisung>)?
• Semantik:
? der Ausdruck muss vom Typ boolean sein
? falls die Auswertung des Ausdrucks true liefert,
wird die erste Anweisung ausgeführt
? andernfalls die zweite (sofern vorhanden)
• im Zweifel gehört ein else-Zweig zum innersten if
• Beispiel: if (x>=0) if (x>0) x = 0; else x = 1;
35
Deklaration lokaler Variablen
• lokale Variablen werden in einer Anweisungsfolge (einer Methode) deklariert
• die Deklaration enspricht syntaktisch der von Attributen,
jedoch ohne Sichtbarkeitsangabe
• Beispiel: Person seth = new Person("Seth",adam,eva);
• Syntax:<Anweisung> ::= <Variablen-Deklaration> | . . .
<Variablen-Deklaration> ::= <Typ> <Variablenname> (= <Ausdruck>)? ;
• Semantik:
? reserviert benannten Speicherplatz, der nur während der Abarbeitung der
zugehörigen Anweisungsfolge bereitsteht
? außerhalb der Anweisungsfolge ist die Variable unbekannt
36
2.2 Basistypen und Basisoperationen
• Java unterstützt die folgenden Basistypen
Ganze Zahlen
byte
short
int
long
Gleitkommazahlen
float
double
sonstige
char
boolean
Bits
8
16
32
64
-263
Bereich
-128 .. 127
-32768 .. 32767
-231 .. (231 − 1)
.. (263 − 1), z.B. 42L
32
64
+/- 3.4E+38 .. +/- 1.4E-45
+/- 1.7E+308 .. +/-4.9E-324
16
?
Unicode-Zeichen
true, false
37
Arithmetische Operationen (auf Zahlen)
• binär (infix): +,-,*,/,%
• unär: --,++ (präfix oder postfix), +,• Beispiele: x * (-1), x++, ++x, 1 + 1.0 (liefert: 2.0)
38
Relationale Operationen
• binär, infix
• >,<,>=,<=,==,!=,instanceof
• Ergebnistyp: boolean
• Vor.: Operanden haben “kompatiblen Typ”
• Beispiel: x >= 0, kunde1 instanceof Konto
39
Boolesche Operationen
• Operanden und Ergebnis boolean
• binär (infix):
&&: nicht-striktes “und”
2. Argument wird nur ausgewertet, wenn 1. Argument true
||: nicht-striktes “oder”
2. Argument wird nur ausgewertet, wenn 1. Argument false
&: striktes “und”; beide Argumente werden ausgewertet
|: striktes “oder”; beide Argumente werden ausgewertet
• unär: !: “nicht”; in Präfixnotation, z.B. ! fertig
40
Beispiel: Strikte vs. nicht-strikte Operationen
1) if (x > 1 && y % x-- > 0) x = y;
2) if (x > 1 & y % x-- > 0) x = y;
• wenn x den Wert 0 hat: 2) Absturz, 1) nicht
41
Verzweigung auf Ausdruck-Ebene
statt: if (x == 0) x = 1; else x = 1/x;
auch: x = (x == 0) ? 1 : 1/x;
allgemein: <Ausdruck> ::= ( <Ausdruck>0 ) ? <Ausdruck>1 : <Ausdruck>2
• falls <Ausdruck>0 zu true ausgewertet wird,
ist der Wert von <Ausdruck>1 das Ergebnis
• sonst der von <Ausdruck>2
• nur einer von beiden Ausdrücken wird ausgewertet
42
Bit-Operationen
• Vor.: Argumente und Ergebnis byte, short, int oder long
• op1 >> op2
Binärdarstellung von op1 wird um op2 Positionen nach rechts geschoben
• op1 << op2
Linksshift, analog
• weiterhin: & (bitweises “und”), | (bitweises “oder”),
^ (bitweises “xor”), ˜ (bitweises Komplement, unär)
• geeignet zur effizienten Implementierung von Mengenoperationen
• Beispiele: 7>>1 =
ˆ 3, 6&3 =
ˆ 2
43
Zuweisungsoperationen
• Grundform: x = e
• außerdem: x += e =
ˆ x = x + e
• analog: -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
• Beispiele:
? x += 1 =
ˆ x = x + 1 =
ˆ x++
? x *= 2 =
ˆ x = x * 2
44
Operatorpräzedenz
Operation
[] . (params) expr++ expr-++expr --expr +expr -expr ˜ !
new (type)expr * / %
+<< >> >>>
• absteigende Präzedenz, z.B.
< > <= >= instanceof
-x+y*2 == z<<1 && !b =
ˆ
== !=
&
(((-x)+(y*2)) == (z<<1)) && (!b)
^
|
&&
||
?:
= += usw.
45
Konstanten
• werden Attribute als final gekennzeichnet, so ist ihr Wert unveränderlich
• sie entsprechen damit benannten Konstanten
• Beispiel: private final int Mehrwertsteuer = 16;
• zur besseren Verständlichkeit und Wartungsfreundlichkeit sollten in Anweisungen
keine unbenannten Konstanten (außer 0, 1, 2) verwendet werden
Beispiel: Brutto = Netto * (Mehrwertsteuer + 100)/100;
46
Fallen der Rechnerarithmetik
• Rechnerarithmetik ist i.allg. nicht assoziativ und nicht kommutativ
Beispiel:
4.0 * 8E+307 /2.0
→ Infinity
4.0 * (8E+307 /2.0)
→ 1.6E+308
• bei Rechnerarithmetik entstehen Rundungsfehler
• ⇒ Gleitkommazahlen nicht auf Gleichheit oder Ungleichheit testen!
Beispiel:
statt:
besser:
if (x != 1.0) ...
final float epsilon = 0.0001; ...
if (Math.abs(x - 1.0) > epsilon) ...
47
2.3 Felder (Arrays)
Motivation
public class FirmaNaiv{
private int umsatzM1;
private int umsatzM2;
...
private int umsatzM12;
• häufig: viele gleichartige Daten
public int jahresUmsatz(){
int summe = 0;
summe += umsatzM1;
summe += umsatzM2;
...
summe += umsatzM12;
return summe;}
• naiv und umständlich:
jeweils eigene Variable
• Beispiel:
// weitere Methoden
}
48
Jahresumsatz-Beispiel mit Arrays
public class Firma{
private int[] umsatz = new int[12];
public int jahresUmsatz(){
int summe = 0;
for (int i = 0; i < umsatz.length; i++)
summe += umsatz[i];
return summe;}
// weitere Methoden
}
49
Eigenschaften von Arrays
• verwendet bei mehreren gleichartigen Daten (Vor.: feste oder begrenzte Anzahl)
• Zugriff auf Element über Index, z.B. umsatz[i]
• zugehörige Kontrollstruktur meist for-Schleife (s.u.)
• für ein Array a liefert a.length die Anzahl der Elemente
• der Indexbereich läuft von 0 bis a.length-1
• Achtung: Indexbereich nicht verlassen!
• ein Arrayelement kann wie eine Variable verwendet werden
• new <Typ>[n] erzeugt ein Array mit n Elementen vom angegeben Typ
50
Erweiterungen der Syntax
• <Typ> ::= <Typ>[<Ausdruck>?] | . . .
• <Ausdruck> ::= <Arrayname>[<Ausdruck>] | new <Typ> . . .
• <Zuweisung>::=
<Arrayname>[<Ausdruck>] <Zuweisungsoperator> <Ausdruck>; | . . .
51
Zuweisung von Arrays
• bei einer Zuweisung von Arrays werden nicht die Elemente
sondern nur der Verweis auf das Array kopiert
• beide Arrays arbeiten im folgenden auf den gleichen Speicherplätzen, Beispiel:
int[] a = new int[12];
// erzeugt
a[5] = 42;
int[] b;
// erzeugt einen Verweis
b = a;
// jetzt verweisen a und
System.out.println("b[5]:"+b[5]);
// liefert
b[5] = 56;
System.out.println("a[5]:"+a[5]);
// liefert
ein Array; a verweist darauf
auf ein Array, aber kein Array
b auf das gleiche Array
42
56
52
Zählschleifen
• Idee: eine Anweisung wird wiederholt ausgeführt
• Beispiel: for (int i=1; i <= n; i++) fakultaet *= i;
• geeignet, wenn Anzahl der Schleifendurchläufe vorab bekannt
• Syntax:
<Anweisung> ::= <for-Schleife> | . . .
<for-Schleife> ::=
for ( <Ausdruck>0 ; <Ausdruck>1 ; <Ausdruck2> ) <Anweisung>
53
Semantik:
• Initialisierung:
? <Ausdruck>0 wird am Anfang der Schleife ausgewertet
? typischerweise wird hierbei eine Zählvariable (bereitgestellt und) initialisiert
• Abbruchbedingung:
?
?
?
?
?
<Ausdruck>1 muss boolean sein
er wird vor jeder Abarbeitung der Anweisung ausgewertet
liefert <Ausdruck>1 den Wert false, wird die Schleife beendet
anderfalls wird die Anweisung ein (weiteres) Mal ausgeführt
ist <Ausdruck>1 direkt false, wird die Anweisung nie ausgeführt
• Inkrementierung:
? <Ausdruck>2 wird nach jeder Abarbeitung der Anweisung ausgewertet
? typischerweise wird hier die Zählvariable hochgezählt
54
2.4 Kontrollstrukturen
• bestimmen, in welcher Reihenfolge die Anweisungen ausgeführt werden
• elementare Anweisung: Zuweisung, Methodenaufruf, Variablendeklaration
• Sequenz
• Verzweigung: if, Mehrfachverzweigung (switch)
• Schleife: for-, while- und do-while-Schleife
• Rekursion
• Exceptions (später)
• Threads (später)
55
Mehrfachverzweigung
Beispiel:
int wochentag;
...
switch (wochentag){
case 1: System.out.println(" vormittags" );
case 2: case 3: case 4: case 5:
System.out.println(" geöffnet" ); break;
default: System.out.println(" geschlossen" );}
• Syntax:
<Anweisung> ::= <switch-Anweisung> | break ; . . .
<switch-Anweisung> ::= switch ( <Ausdruck> ) { <Fall>+
(default : <Anweisung>∗)? }
<Fall> ::= case <Konstante> : <Anweisung>∗
56
Semantik:
• der Ausdruck muss vom Typ int sein
• die Konstanten müssen (unbenannte) ganze Zahlen sein
• zunächst wird der Ausdruck ausgewertet
• dann wird der Fall ermittelt, dessen Konstante dem erhaltenen Wert entspricht
• die Anweisungen dieses Falls und der folgenden Fälle (!) werden sukzessiv
abgearbeitet, bis eine break-Anweisung erreicht wird
• durch eine break-Anweisung wird die switch-Anweisung beendet
(und die hierauf folgende Anweisung bearbeitet)
• wird kein passender Fall gefunden, so werden die Anweisungen des default-Falls
ausgeführt (sofern vorhanden)
57
while-Schleife
Beispiel:
while (x > y)
x = x - y;
berechnet x mod y (für x>0, y>0)
• geeignet bei komplizierter Abbruchbedingung und wenn #Durchläufe unbekannt
• Syntax:
<Anweisung> ::= <while-Anweisung> | <do-while-Anweisung> . . .
<while-Anweisung> ::= while ( <Ausdruck> ) <Anweisung>
• Semantik:
? solange der (boolesche) Ausdruck true liefert,
wird die Anweisung ausgeführt (ggfs. gar nicht)
? die Überprüfung findet vor Ausführung der Anweisung statt
58
do-while-Schleife
Beispiel:
do x = x - y;
while (x > y);
• geeignet, wenn #Durchläufe vorab unbekannt aber > 0
• Syntax:
<do-while-Anweisung> ::= do <Anweisung> while <Ausdruck> ;
• Semantik:
? führt die Anweisung aus, solange der (boolesche) Ausdruck true liefert
(mindestens ein Mal)
? die Überprüfung findet nach Ausführung der Anweisung statt
59
Rekursion
• oft lässt sich die Lösung eines Problems auf die Lösung eines gleichartigen
(“kleineren”) Problems zurückführen
• hierzu eignen sich rekursive (d.h. sich selbst aufrufende) Methoden
Beispiel: Türme von Hanoi
public static void turm(int n,
int quelle, int ziel, int hilf){
if (n >= 1) {
turm(n-1,quelle,hilf,ziel);
System.out.println("von " +quelle+
" nach " +ziel);
turm(n-1,hilf,ziel,quelle);}}
Aufruf z.B. turm(2,1,2,3)
60
Weiterführung des Verwandschaft-Beispiels
public class Person{
private String name;
private Person vater;
private Person mutter;
private Person[] kind = new Person[10];
private int kinder = 0;
public Person(String n, Person v, Person m){
name = n; vater = v; mutter = m;
if (vater != null) vater.addKind(this);
if (mutter != null) mutter.addKind(this);}
public void addKind(Person neugeboren){kind[kinder++] = neugeboren;}
//getName, getVater, getMutter wie bisher; vorfahren, nachkommen s.u.
public static void main(String[] args){
// Person adam, eva, seth, lamech, noah wie bisher
noah.vorfahren(); eva.nachkommen();}
}
61
public void vorfahren(){
if (vater != null){
System.out.println(vater.getName());
vater.vorfahren();}
if (mutter != null){
System.out.println(mutter.getName());
mutter.vorfahren();}}
public void nachkommen(){
for(int i=0; i<kinder; i++){
System.out.println(kind[i].getName());
kind[i].nachkommen();}}
62
2.5 Objektorientierung
Motivation
• ein Großteil der IT-Kosten für Wartung
• OO senkt Wartungskosten
? da zusammengehörige Codestücke nicht verstreut,
sondern in Klassen zusammengefasst
? Änderungen bleiben dadurch oft lokal eingegrenzt
• Entwicklungskosten lassen sich durch Wiederverwendung deutlich senken
• OO unterstützt Wiederverwendung durch
? inhaltliche Strukturierung in Klassen
? Vererbung (s.u.) (→ Frameworks)
63
Motivation (Fortsetzung)
• Softwareentwicklung erfolgt in Teams
• Teamarbeit erfordert klare Schnittstellen
• Klassen bieten klare Schnittstellen (Datenabstraktion, Kapselung)
? nur Entwickler kennen Implementierung (Attribute und Methodenrümpfe)
? Anwender kennen nur die Schnittstelle, d.h.
Name, Parametertypen, Ergebnistyp und Spezifikation
jeder öffentlichen (public) Methode
? Änderungen der Implementierung haben für die Anwender keine Auswirkungen,
solange die Schnittstelle bleibt
64
Grundbegriffe der Objektorientierung
• Klasse, Objekt, Attribut, Methode: vgl. 2.1
• Nachrichtenaustausch, Methodenaufruf: vgl. 2.1
• Vererbung, Vererbungspolymorphismus, spätes Binden: s.u.
65
Beispiel: Naive Programmierung ohne Kapselung
public class Punkt{
public double x;
public double y;
public Punkt(double x, double y){
this.x = x; this.y = y;}
}
public class Strecke{
public Punkt anfang;
public Punkt ende;
public double laenge;
public Strecke(Punkt p1, Punkt p2){
anfang = p1; ende = p2;
laenge = Math.sqrt(
Math.pow(p1.x - p2.x,2.0)
+ Math.pow(p1.y - p2.y,2.0));}
}
möglicher Anwendercode:
...
Punkt p1 = new Punkt(1.0,1.0);
Punkt p2 = new Punkt(4.0,5.0);
Strecke s = new Strecke(p1,p2);
System.out.println("Anfangspunkt: ("
+p1.x+","+p1.y+")");
System.out.println("Endpunkt: ("
+p2.x+","+p2.y+")");
System.out.println("Laenge: "+
s.laenge);
...
Problem:
• eine Änderung der Implementierung von Strecke oder Punkt
erfordert umfangreiche Anpassungen des Anwendercodes
66
public class Punkt{
private double x;
private double y;
public Punkt(double x, double y){
this.x = x; this.y = y;}
public double getX(){return x;}
public double getY(){return y;}
}
public class Strecke{
private Punkt anfang;
private Punkt ende;
public Strecke(Punkt p1, Punkt p2){
anfang = p1; ende = p2;}
public double getLaenge(){
return Math.sqrt(
Math.pow(anfang.getX()
- ende.getX(),2.0)
+ Math.pow(anfang.getY()
- ende.getY(),2.0));}
}
Programmierung mit Kapselung
möglicher Anwendercode:
Punkt p1 = new Punkt(1.0,1.0);
Punkt p2 = new Punkt(4.0,5.0);
Strecke s = new Strecke(p1,p2);
System.out.println("Anfangspunkt: ("
+p1.getX()+","+p1.getY()+")");
System.out.println("Endpunkt: ("
+p2.getX()+","+p2.getY()+")");
System.out.println("Laenge: "
+s.getLaenge());
• für den Anwender ist egal,
? dass die Länge hier (aus Platzgründen?) nicht gespeichert,
sondern jedesmal berechnet wird
? ob ein Punkt kartesisch oder
mit Polarkoordinaten dargestellt
wird
67
Vererbung
• häufig ist eine Klasse A eine Spezialisierung (durch zusätzliche Attribute und
Methoden) einer anderen Klasse B
• um die vorhandenen Methoden wiederverwenden zu können,
kann A als Unterklasse von B deklariert werden (mit extends)
• Unterklassen erben Struktur und Verhalten ihrer Oberklassen
• ggf. nicht (völlig) passende Methoden von A können in B überschrieben werden
• die gleiche Methode kann auf Objekten der Ober- und Unterklasse ausgeführt
werden (Vererbungs-Polymorphismus)
• erst zur Laufzeit wird ermittelt, ob die Oberklassenoperation oder
eine gleichnamige Unterklassenoperationen anzuwenden ist (→ spätes Binden)
68
Vererbung (Fortsetzung)
• enthält die Klasse des Empfängerobjekts einer Nachricht keine entsprechende
Methode, so wird die gleichnamige Methode der nächstgelegenen Oberklasse
verwendet
• in Java hat jede Klasse höchstens eine Oberklasse
• (Ober-)Klassen ohne Instanzen heißen abstrakte Klassen
• abstrakte Klassen erlauben die Zusammenfassung gemeinsamer Teilstrukturen
und gemeisamen Verhaltens ähnlich aufgebauter (Unter-)Klassen
69
Beispiel: Vererbung
public class Punkt{
protected double x,y;
public Punkt(double x1, double y1){
x = x1; y = y1;}
public class FarbPunkt extends Punkt{
// erbt x,y,getX,getY,
// verschiebe,inRechteck
protected int farbe;
public void getX(){return x;}
public FarbPunkt(double x1, double y1,
int f){
super(x1,y1); farbe = f;}
public void getY(){return y;}
public void verschiebe(double dx,
double dy){
x = x+dx; y = y+dy;}
public boolean inRechteck(
double minx, double maxx,
double miny, double maxy){
return ((minx <= x) && (x <= maxx) &&
(miny <= y) && (y <= maxy));}
}
public getFarbe(){return farbe;}
}
70
Anwendung:
FarbPunkt p = new FarbPunkt(1.0,2.0,42);
System.out.println("x: "+p.getX());
p.verschiebe(2.0,2.0);
System.out.println(p.inRechteck(2.0,4.0,3.0,4.0));
System.out.println("Farbe: "+p.getFarbe());
• beachte: auf FarbPunkte auch Methoden der Oberklasse Punkt anwendbar
• durch super(x1,y1) wird der Konstruktor der Oberklasse aufgerufen
• Syntax: <Klasse> ::= <Sichtbarkeit> class <Klassenname>
extends <Klassenname> { <Attribut-Deklaration>∗ <Methode>∗}
<Sichtbarkeit> ::= protected | . . .
• Attribute sollten i.d.R. die Sichtbarkeit protected (statt private) haben
• dann werden sie von den Unterklassen geerbt (bei private nicht!)
71
Beispiel: Überschreiben
public class Kreis extends Punkt{
protected double r;
public Kreis(double x1,double y1,double r1){
super(x1,y1); r = r1;}
public double flaeche(){return Math.PI*r*r;}
public boolean inRechteck(){
double minx, double maxx,
double miny, double maxy){
return ((minx <= x-r) && (x+r <= maxx) &&
(miny <= y-r) && (y+r <= maxy));}
}
72
Vererbung im Klassendiagramm
Punkt
x
y
getX
getY
verschiebe
inRechteck
FarbPunkt
farbe
getFarbe
Kreis
r
flaeche
inRechteck
• Vererbung dargestellt durch Pfeil mit offener Spitze
von der Unterklasse zur Oberklasse
73
Unterklassen und Vererbung
in Unterklasse möglich:
• zusätzliche Attribute und Methoden
• eingeschränkte Wertebereiche der Attribute
• Änderung der Implementierung von Methoden der Oberklasse
nicht möglich:
• Weglassen von Attributen oder Methoden der Oberklasse
• Änderung der Parameter von Methoden
74
Diskussion von Vererbung
• Vorteil: Vererbung erlaubt die Wiederverwendung von Code
• Nachteil: Vererbung untergräbt die Datenkapselung
?
?
?
?
um Unterklassen zu verstehen, müssen Oberklassen bekannt sein
Lokalität von Änderungen geht verloren
Änderung von Oberklasse kann Änderung von Unterklassen erfordern
daher: Vererbung behutsam einsetzen
(möglichst) nur bei Spezialisierung
? insbesondere: Oberklassenoperationen möglichst nicht überschreiben
• Vererbung vereinfacht das Testen nicht
75
Spätes Binden
boolean alleImRechteck(Punkt[] punkte, double minx, double maxx,
double miny, double maxy){
for(int i = 0; i < punkte.length; i++)
if (!punkte[i].inRechteck(minx,maxx,miny,maxy))
return false;
return true;}
• punkte kann neben Objekten der Klasse Punkt auch Objekte einer Unterklasse
(z.B. Kreis) enthalten
• welche Methode inRechteck jeweils aufgerufen wird,
hängt ab von der Klasse von punkte[i]
• Auswahl der Methode zur Laufzeit (spätes Binden)
• Vorteil: alleImRechteck muß beim Hinzufügen weiterer Unterklassen von
Punkt nicht geändert werden
76
Schlechter Stil: Programmierung ohne spätes Binden
boolean alleImRechteck(Object[] punkte, double minx, double maxx,
double miny, double maxy){
for(int i = 0; i < punkte.length; i++)
if (punkte[i] instanceof Kreis){
if (! ((Kreis) punkt[i]).inRechteck(minx,maxx,miny,maxy))
return false;}
else if (punkte[i] instanceof Punkt)
if (! ((Punkt) punkt[i]).inRechteck(minx,maxx,miny,maxy))
return false;}
return true;}
Problem:
• alleImRechteck bezieht sich explizit auf alle hier möglichen Klassen
• bei Änderung der Klassenhierarchie von Punkt: aufwändige Anpassung
• Bemerkung: in Nicht-OO-Sprachen lässt sich dieser Stil nicht vermeiden
77
Klassenvariablen und Klassenmethoden
• bisher: Methoden angewendet auf Objekte
• nun auch: an Klasse (statt Instanzen) gebundene Variablen und Methoden
• Klassenvariablen und -methoden werden durch static gekennzeichnet
public class Punkt{
static int num punkte = 0;// Klassenvariable
protected double x,y;
// Attribute
public void Punkt(double x1, double y1){
x = x1; y = y1; num punkte++;}
public static Punkt lexgeq(Punkt p, Punkt q){ // Klassenmethode
if ((p.x >= q.x) || ((p.x == q.x) && (p.y >= q.y))) return p;
else return q;}
}
78
Verwendung von Klassenmethoden und Klassenvariablen
Beispiele:
Punkt p = new Punkt(1.0,2.5);
Punkt q = new Punkt(1.0,3.2);
Punkt v = Punkt.lexgeq(p,q);
System.out.println("pi:"+Math.PI);
• System.out und Math.PI sind (public) Klassenvariablen
der Bibliotheksklassen System bzw. Math
•Syntax:
<Ausdruck> ::= <Klassenname>.<Klassenvariablenname> | . . . |
<Klassenname>.<Klassenmethodenname>((<Ausdruck> (, <Ausdruck>)∗)?)
79
Interfaces
• einige OO-Sprachen (z.B. C++) erlauben, dass eine Klasse von mehreren
Oberklassen erbt → ggfs. Namenskonflikte
• in Java:
? nur eine Oberklasse (implizit Object)
? Simulation von Mehrfachvererbung durch Implementation von Interfaces
• Interface: =
ˆ abstrakter Klasse mit ausschließlich abstrakten Methoden
• zur Implementierung eines Interfaces müssen
alle zugehörigen Methoden implementiert werden
80
Beispiel: Interfaces
public class Wasserfahrzeug{
protected double tiefgang;
public Wasserfahrzeug(double tg){tiefgang = tg;}
public double getTiefgang(){return tiefgang;}}
public interface Flugobjekt{
public abstract void setFlughoehe(double h);
public abstract double getFlughoehe();}
public class Wasserflugzeug extends Wasserfahrzeug implements Flugobjekt{
protected double flughoehe = 0.0;
public Wasserflugzeug(double tg){super(tg);}
public void setFlughoehe(double h){flughoehe = h;}
public double getFlughoehe(){return flughoehe;}}
81
Verwendung:
Wasserflugzeug w = new Wasserflugzeug(0.52);
Flugobjekt f = w;
f.setFlughoehe(f.getFlughoehe() + 100.0);
System.out.println(w.getFlughoehe()+","+
w.getTiefgang()+","+
f.getFlughoehe());
Wasserfahrzeug
tiefgang
getTiefgang
setTiefgang
<<interface>>
Flugobjekt
getFlughoehe
setFlughoehe
Wasserflugzeug
flughoehe
82
Syntax-Erweiterungen
<Interface-Deklaration> ::= <Sichtbarkeit> interface <Interface-Name> {
<Methode>∗}
<Klasse> ::= <Sichtbarkeit> abstract? class <Klassenname>
extends <Klassenname>
implements <Interface-Name> (, <Interface-Name>)∗
{ <Attribut-Deklaration>∗ <Methode>∗}
<Methode> ::= <Sichtbarkeit> abstract (<Typ>| void) Methodenname
(<Parameterliste>) ; | . . .
83
2.6 Graphische Benutzerschnittstellen
• Benutzerschnittstellen früher: aktiver Frage-/Antwort-Dialog
• heute: graphische Benutzerschnittstelle (GUI) bestehend aus
Fenstern, Menüs, Knöpfen, Texteingabefeldern, . . .
• ereignisorientiert: jede GUI-Komponente wartet auf das Eintreten relevanter
Ereignisse (z.B. Mausklick, Tastendruck, Knopfdruck), interpretiert sie und
bearbeitet sie
• die Art und Reihenfolge der Ereignisse ist nicht vorhersehbar
• daher keine aktive Dialogsteuerung sondern passive Ereignisbehandler,
die durch ein Ereignis aktiviert werden
84
Framework
• objektorientierte Klassenbibliothek
• stellt aufgabenbezogene Grundfunktionalität (System von Klassen) bereit
• anpassbar durch Ergänzung eigener Unterklassen
• Beispiele für GUI-Frameworks: Java AWT, Swing (mächtiger; basiert auf AWT)
85
Erstellung einer graphischen Benutzerschnittstelle
• Anpassung eines GUI-Frameworks durch Bereitstellung eigener Unterklassen
• Aufbau eines Objektnetzes, das alle GUI-Komponenten
und die Fachkonzeptklassen verknüpft
• insbesondere: Ereignisbehandler implementieren
• statt textuell kann die GUI auch mit einer visuellen Entwicklungsumgebung
(z.B. Forte) graphisch am Bildschirm entworfen und der zugehörige Java-Code
automatisch generiert werden
86
Beispiel: Zähler
Zählerstand:
Weiterzählen
import java.awt.*;
import java.awt.event.*;
Zurücksetzen
Beenden
public class Zaehler extends Frame
implements ActionListener{
protected
protected
protected
protected
TextField zstand
Button weiter
Button zurueck
Button ende
=
=
=
=
new
new
new
new
TextField("0",4);
Button("Weiterzaehlen");
Button("Zuruecksetzen");
Button("Beenden");
public Zaehler(){... s.u. ...}
public void actionPerformed(ActionEvent event){... s.u. ...}
static public void main(String[] args){
new Zaehler();}
}
87
public Zaehler(){
setBounds(400,400,250,300);
setLayout(new FlowLayout());
setBackground(Color.white);
add(new Label("Zaehlerstand: "));
add(zstand); add(weiter);
add(zurueck); add(ende);
weiter.addActionListener(this);
zurueck.addActionListener(this);
ende.addActionListener(this);
pack(); show();}
public void actionPerformed(
ActionEvent event){
if (event.getSource() == weiter){
int wert = Integer.parseInt(
zstand.getText());
zstand.setText(""+(wert+1));}
else if (event.getSource() == zurueck)
zstand.setText("0");
else if (event.getSource() == ende)
dispose();}
88
Beispiel: SimpelBank mit graphischer Benutzerschnittstelle
Wilkommen bei der SimpelBank
Kontonr.
Geheimzahl
Was wollen Sie tun? Beenden
Überweisen
Ein-/Auszahlen
Überweisung
Konto des Empfängers
Beenden
Ein-/Auszahlung
Betrag
Betrag
ok
ok
• Dialog beschrieben durch endlichen Automaten (UML Statechart)
89
Klasse MeinFenster
import java.awt.*;
import java.awt.event.*;
public abstract class MeinFenster extends Frame{
public static void put(Container ctr, Component comp,
int x, int y, int w){
GridBagLayout g = (GridBagLayout) ctr.getLayout();
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.gridx = x;
c.gridy = y; c.gridwidth = w; c.gridheight = 1;
c.weightx = 0.0; c.weighty = 0.0; ctr.add(comp);
g.setConstraints(comp,c);}
public MeinFenster(String titel)
setTitle(titel);
setBounds(400,400,250,300);
setLayout(new GridBagLayout());
setBackground(Color.white);
}
90
Klasse Willkommen
import java.awt.*;
import java.awt.event.*;
public class Willkommen extends MeinFenster
implements ItemListener{
private class UeberweisenFenster extends MeinFenster
implements ActionListener{...s.u. ...}
private class EinAuszahlFenster extends MeinFenster
implements ActionListener{...analog zu UeberweisenFenster...}
protected TextField konto = new TextField(8);
protected TextField pin
= new TextField(8);
protected Choice funktion = new Choice();
public Willkommen(){... s.u. ...}
public void itemStateChanged(ItemEvent event){... s.u. ...}
static public void main(String[] args){
new Konto(1001,50); new Konto(990,50); new Willkommen();}
}
91
public Willkommen(){
super("Willkommen bei der SimpelBank");
funktion.addItem("Beenden");
funktion.addItem("Ueberweisen");
funktion.addItem("Einzahlen");
funktion.addItem("Auszahlen");
put(this,
put(this,
put(this,
put(this,
put(this,
new Label("Kontonr.: "),
konto,
new Label("Geheimzahl: "),
pin,
funktion,
funktion.addItemListener(this);
pack(); show();}
0,0,1);
1,0,2);
0,1,1);
1,1,2);
1,2,1);
92
public void itemStateChanged(ItemEvent event){
String Operation = funktion.getSelectedItem();
if (Operation.equals("Beenden")) {dispose(); return;}
Konto kunde = Konto.suche(Integer.parseInt(konto.getText()));
if (kunde == null) {konto.setText("falsch"); return;}
if (! kunde.checkPin(Integer.parseInt(pin.getText()))){
pin.setText("falsch"); return;}
if (Operation.equals("Ueberweisen"))
new UeberweisenFenster(kunde);
else if (Operation.equals("Einzahlen"))
new EinAuszahlFenster(kunde,true);
else if (Operation.equals("Auszahlen"))
new EinAuszahlFenster(kunde,false);}
93
Klasse UeberweisenFenster
public class UeberweisenFenster extends MeinFenster implements ActionListener{
protected
protected
protected
protected
TextField empfaenger = new TextField(8);
TextField betrag
= new TextField(8);
Button ok = new Button("ok");
Konto kunde;
public UeberweisenFenster(Konto kunde){
super("Ueberweisung");
this.kunde = kunde;
put(this, new Label("Kontonr. des Empfaengers: "), 0,0,1);
put(this, empfaenger,
1,0,2);
put(this, new Label("Betrag: "),
0,1,1);
put(this, betrag,
1,1,2);
put(this, ok,
1,2,1);
ok.addActionListener(this);
pack(); show();}
94
public void actionPerformed(ActionEvent event){
if (event.getSource() == ok){
int wert = Integer.parseInt(betrag.getText());
Konto ziel = Konto.suche(Integer.parseInt(empfaenger.getText()));
if (ziel == null){empfaenger.setText("?"); return;}
Ueberweisung ueberweisung = new Ueberweisung(kunde,ziel,wert);
dispose();}}
}
95
Kontroll-Klasse Ueberweisung
public class Ueberweisung{
public Ueberweisung(Konto kunde, Konto ziel, int betrag){
kunde.setSaldo(kunde.getSaldo()-betrag);
ziel.setSaldo(ziel.getSaldo()+betrag);}
}
• sinnvoll: Ablauf jedes Geschaftsvorfalls in eigener Klasse kapseln
• so bleiben auch Änderungen der Funktionalität lokal begrenzt
96
Datenhaltungsklasse Konto
public class Konto{
protected static final int maxAnzahl = 10000;
protected static int anzahl = 0;
protected static Konto[] konten = new Konto[maxAnzahl]; //in Praxis: Datenbank
protected int nr;
protected int pin;
protected int saldo;
public Konto(int nr, int betrag){ konten[anzahl++] = this;
pin = nr % 9735; saldo = betrag;}
public
public
public
public
int getNr(){return nr;}
int getSaldo(){return saldo;}
void setSaldo(int betrag){saldo = betrag;}
boolean checkPin(int eingabe){return pin == eingabe);}
public static Konto suche(int knr){
for (int i=0; i<anzahl; i++)
if (konten[i].getNr() == knr) return konten[i];
return null;}
}
this.nr = nr;
97
Bemerkungen:
• eine Methode mit Ergebnistyp void kann durch return; verlassen werden
• bei einem Array von Objekten (z.B. konten)
werden zunächst nur null-Referenzen angelegt
• jedes (Konten-)Objekt muss separat angelegt werden
• Hilfsklasse (wie UeberweisenFenster) z.B. als innere Klasse realisierbar
• ist sie private oder protected, dann ist sie von außen nicht sichtbar
• ist sie nicht static, kann sie auf die Attribute des Objekts der umgebenden
Klasse zugreifen
98
Innere Klassen
• eingeschachtelte static-Klassen über Pfad verfügbar (sofern: public)
Bsp.: List.ListElem
• in eingeschachtelter nicht-static-Klasse (“member class”):
Attribute des zugehörigen Objektes der umgebenden Klasse zugreifbar
• lokale Klasse:
? in Methodenrumpf eingeschachtelte Klasse
? Zugriff auf private-Attribute des zugehörigen Objektes der umgebenden Klasse
wie bei Member-Klasse
? ggfs. anonym
• typische Verwendung: Ereignisbehandler-Klassen
99
Beispiel: Listen
public class List {
private static class ListElem{
ListElem succ;
// Rekursion!
Object content;
ListElem(Object cont, ListElem next){
succ = next; content = cont;}}
protected ListElem head = null;
protected ListElem current = null;
public
public
public
public
public
public
public
public
}
List(){super();}
boolean isEmpty() { return head == null; }
Object getValue(){return current.content;}
void setValue(Object val){current.content = val;}
void first(){current = head;}
void next(){current = current.succ;}
boolean atEnd(){return (current == null);}
void insertFirst(Object item){head = new ListElem(item, head);}
100
2.7 Ausnahmebehandlung
• umständliche naive Fehlerbehandlung:
? jede Methode liefert Information über erfolgreiche Ausführung
? nach Aufruf: Fehlercheck und ggfs. Reaktion
• besser: bei Fehler Exception auslösen und an geeigneter Stelle abfangen
• kein umständliches Durchreichen von Erfolgsinformationen
• neben benutzerdef. Exception-Klassen gibt es systeminterne, z.B.
ArithmeticException, ClassCastException, NullPointerException
101
Syntaxerweiterungen
<Anweisung> ::= try <Anweisungsfolge>
(catch (<Klassenname> <Variablenname> <Anweisungsfolge>)+
(finally <Anweisungsfolge>)?
| throw <Ausdruck>? ;
<Anweisungsfolge> ::= { <Anweisung>∗ }
<Methode> ::= <Sichtbarkeit> abstract? (<Typ>| void) Methodenname
(<Parameterliste>) (throws <Klassenname> (, <Klassenname>)∗)?
. . . Rumpf wie bisher . . .
102
Semantik
• throw löst eine Exception aus
• hinter throw: Instanz (einer Unterklasse) von Exception
• eine Methode muss alle explizit ausgelösten Exceptions, die in ihrem Rumpf (ggf.
indirekt) auftreten können, abfangen oder hinter der Parameterliste angeben
• durch catch kann eine “typmäßig passende” Exception abgefangen werden,
die in der hinter try folgenden Anweisungsfolge ausgelöst wurde
• die auf catch folgende Anweisungsfolge wird dann ausgeführt
• mehrere catch-Böcke (für verschiedene Exceptions) sind möglich
• an “innerster passender” Abfangstelle wird die Programmausführung fortgesetzt
• falls finally-Block: immer ausgeführt (mit oder ohne Exception)
103
Beispiel: naive Fehlerbehandlung ohne Exception
import java.lang.*;
class Foo{
Foo(){
List liste = new List();
for (int i=0; i<10; i++) liste.insertFirst(new Integer(i%4));
liste.first();
if (!listDiv(liste)) System.out.println("Fehler");}
static boolean listDiv(List l){
if (l.atEnd()) return true;
int val = ((Integer) l.getValue()).intValue();
if (val == 0) return false;
else {l.setValue(new Integer(100/val));
l.next(); return listDiv(l);}
}
104
sonstige Bemerkungen zum Beispiel:
• um universell verwendbar zu sein, verwendet List Elemente vom Typ Object
• dadurch: Simulation von parametrischem Polymorphismus
• Basiswerte sind keine Objekte
• sie müssen mit “Wrapper-Klassen” verpackt werden (z.B. int → Integer)
• da Listenelemente vom Typ Object sind, müssen sie zur Weiterverarbeitung i.a.
in ihren ursprünglichen Typ zurückverwandelt werden (Casting)
• Beispiel: (Integer) l.getValue()
• fehlen Sichtbarkeitsangaben, so wird die Sichtbarkeit package eingestellt
• hierbei sind die Bezeichner in allen Klassen des betreffenden Pakets bekannt
105
Beispiel: Auslösen von Exceptions
class DivisionException extends Exception{
public DivisionException(String s){super(s);}}
class IntException extends Exception{
public IntException(String s){super(s);}}
class Foo{
Foo(){... s.u....}
static void listDiv(List l)
throws DivisionException,IntException{
if (!l.atEnd()) return;
if (! (l.getValue() instanceof Integer))
throw new IntException("keine Integerzahl");
int val = ((Integer) l.getValue()).intValue();
if (val == 0) throw new DivisionException("Dividend 0");
l.setValue(new Integer(100/val)); l.next(); listDiv(l);}
}
106
Beispiel: Abfangen von Exceptions
Foo(){
List liste = new List();
for (int i=0; i<10; i++) liste.insertFirst(new Integer(i%4)); liste.first();
try {listDiv(liste);}
catch (DivisionException e1){System.out.println(e1.getMessage());}
catch (IntException e2){System.out.println(e2.getMessage());}
finally{System.out.println("Ende");}}
Herunterladen