Grundlagen der Programmierung Ziel Einfache Programme in Java schreiben können Inhalte • • • • • Literatur H.Mössenböck: Sprechen Sie Java? dpunkt.verlag, 2005 Klausur Di 1.2.2011, 8:00 - 9:00 Prof. Dr. Hanspeter Mössenböck © H.Mössenböck: Sprechen Sie Java? 3. erweiterte Auflage, dpunkt.verlag, 2005 Anweisungen und Datenstrukturen Grundlagen der Objektorientierung Programmentwurf Rekursion Fehlerbehandlung 2 Worum geht es? Programmieren Grundlagen der Programmierung Prof. H. Mössenböck ein Problem so exakt beschreiben, dass es ein Computer lösen kann kreative Tätigkeit Ingenieurtätigkeit 1. Einführung Nur wenige Leute können gut programmieren Programm = Daten + Befehle 4 Daten und Befehle Daten Programmerstellung Problem Menge adressierbarer Speicherzellen x 17 y 4 z 21 Name Wert Daten sind binär gespeichert (z.B. 17 = 10001) Binärspeicherung ist universell (Zahlen, Texte, Bilder, Ton, ...) 1 Byte = 8 Bit 1 Wort = 4 Byte (manchmal 2 Byte) Befehle Spezifikation Aufgabenstellung Algorithmus Lösungsverfahren Mensch Programm Operationen mit den Speicherzellen Maschinensprache Hochsprache // Lade Zelle x ACC x ACC ACC + y // Addiere Zelle y // Speichere Ergebnis in Zelle z z ACC z = x + y; Compiler Codiertes Lösungsverfahren Maschinenprogramm Lader 5 Algorithmus Variablen Sind benannte Behälter für Werte Schrittweises, präzises Verfahren zur Lösung eines Problems Name x y 99 3 Parameter Können ihren Wert ändern Summiere Zahlen von 1 bis max ( max, sum) 1. sum 0 2. zahl 1 3. Wiederhole, solange zahl 3.1 sum sum + zahl 3.2 zahl zahl + 1 6 x x max 100 x+1 Folge von Schritten Haben einen Datentyp = Menge erlaubter Werte Variablentyp Programm = Beschreibung eines Algorithmus in einer Programmiersprache Zahl Zeichen 7 Werte 17 'a' 54 'x' ... ... Typ Form - in eine Zahlenvariable passen nur Zahlen - in eine Zeichenvariable passen nur Zeichen 8 Anweisungen Anweisungen Wertzuweisung Auswahl (auch Verzweigung, Abfrage, Selektion) x x+1 Variable 1. werte Ausdruck aus 2. weise seinen Wert der Variablen zu j x<y? Ausdruck min x n x min Anweisungsfolge (auch Sequenz) y y min = Minimum von x und y "Ablaufdiagramm" x y z 3 4 x+y Assertion x = 3, y = 4, z = 7 Assertion (Zusicherung) Aussage über den Zustand des Algorithmus an einer bestimmten Stelle 9 Anweisungen Beispiel: Vertauschen zweier Variableninhalte Wiederholung (auch Schleife, Iteration) n 0 x>1? j x n Swap ( x, y) n n x/2 n+1 10 0 h x y x>1 x n x y h Schreibtischtest x 3 2 y 2 3 h 3 x/2 n+1 x 1 n = log2 x0 x 1 n = log2 x0 Alternative Darstellung 11 12 Beispiel: Maximum dreier Zahlen bestimmen Max ( a, b, Beispiel: Euklidscher Algorithmus Berechnet den größten gemeinsamen Teiler zweier Zahlen x und y c, max) GGT ( x, j j max a>c? a a>b? n j c max Schreibtischtest y, ggt) n b>c? a>b c max a b n b a max b rest c Rest von x / y rest c 0 x y rest 28 20 8 20 8 4 8 4 0 x y y rest Rest von x / y rest Warum funktioniert dieser Algorithmus? (ggt teilt x) & (ggt teilt y) x = i*ggt, y = j*ggt, (x-y) = (i-j)*ggt ggt teilt (x - y) ggt teilt (x - q*y) ggt teilt rest GGT(x, y) = GGT(y, rest) rest = 0 ggt y 13 Beschreibung von Programmiersprachen Beispiel: Quadratwurzel von x berechnen 1. 2. Näherung: root x(a/ +2 root) / 2 a x / root 0 a root a root x / 2 a x / root a Syntax x Regeln, nach denen Sätze gebaut werden dürfen z.B.: Zuweisung = Variable " " Ausdruck. root SquareRoot ( x, root) a * root = x root a * root = x root (root + a) / 2 a x / root a * root = x a * root = x & a = root root * root = x 14 Schreibtischtest x root 10 5 3.5 3.17857 3.16232 3.16228 Semantik a Bedeutung der Sätze z.B.: werte Ausdruck aus und weise ihn der Variablen zu 2 2.85714 3.14607 3.16223 3.16228 Grammatik Menge von Syntaxregeln z.B. Grammatik der ganzen Zahlen Kommazahlen sind meist nicht exakt gleich Ziffer = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9". Zahl = Ziffer {Ziffer}. daher besser Ziffer | a-root | > 0.0000001 0 Zahl 1 2 3 4 5 6 7 8 9 Ziffer 15 16 EBNF (Erweiterte Backus-Naur-Form) Metazeichen = . | () [] {} Bedeutung Beispiel trennt Regelseiten schließt Regel ab trennt Alternativen klammert Alternativen wahlweises Vorkommen 0..n-maliges Vorkommen A=xyz. beschreibt Grundlagen der Programmierung Prof. H. Mössenböck x|y (x | y) z [x] y {x} y x, y xz, yz xy, y y, xy, xxy, xxxy, ... 2. Einfache Programme Beispiele Grammatik der Gleitkommazahlen Zahl = Ziffer {Ziffer}. Gleitkommazahl = Zahl "." Zahl ["E" ["+" | "-"] Zahl]. Grammatik der If-Anweisung IfAnweisung = "if" "(" Ausdruck ")" Anweisung ["else" Anweisung]. 17 Grundsymbole Variablendeklarationen Namen bezeichnen Variablen, Typen, ... in einem Programm - bestehen aus Buchstaben, Ziffern und "_" - beginnen mit Buchstaben - beliebig lang - Groß-/Kleinschreibung signifikant Schlüsselwörter - heben Programmteile hervor - dürfen nicht als Namen verwendet werden Zahlen - ganze Zahlen (dezimal oder hexadezimal) - Gleitkommazahlen Zeichketten - beliebige Zeichen zwischen Hochkommas - dürfen nicht über Zeilengrenzen gehen - " in der Zeichenkette wird als \" geschrieben Jede Variable muss vor ihrer Verwendung deklariert werden x x17 myVar my_var - macht den Namen und den Typ der Variablen bekannt - Compiler reserviert Speicherplatz für die Variable if while Ganzzahlige Typen 376 0x1A5 3.14 int x; short a, b; dezimal 1*162+10*161+5*160 Gleitkommazahl byte short int long deklariert eine Variable x vom Typ int (integer) deklariert 2 Variablen a und b vom Typ short (short integer) 8-Bit-Zahl 16-Bit-Zahl 32-Bit-Zahl 64-Bit-Zahl -27 .. 27-1 -215 .. 215-1 -231 .. 231-1 -263 .. 263-1 (-128 .. 127) (-32768 .. 32767) (-2 147 483 648 .. 2 147 483 647) Initialisierungen "a simple string" "sie sagte \"Hallo\"" int x = 100; short a = 0, b = 1; 2 deklariert int-Variable x; weist ihr den Anfangswert 100 zu deklariert 2 short-Variablen a und b mit Anfangswerten 3 Konstantendeklarationen Kommentare Initialisierte "Variablen", deren Wert man nicht mehr ändern kann Geben Erläuterungen zum Programm Zeilenendekommentare static final int max = 100; - beginnen mit // - gehen bis zum Zeilenende Zweck - bessere Lesbarkeit Klammerkommentare max ist lesbarer als 100 - - bessere Wartbarkeit wenn die Konstante mehrmals vorkommt und geändert werden muss, dann muss das nur an 1 Stelle erfolgen Konstantendeklaration muss auf Klassenebene stehen (s. später) durch /* ... */ begrenzt können über mehrere Zeilen gehen dürfen nicht geschachtelt werden oft zum "Auskommentieren" von Programmteilen int sum; // total sales /* Das ist ein längerer Kommentar, der über mehrere Zeilen geht */ Sinnvoll kommentieren! - alles kommentieren, was Erklärung bedarf - statt unklares Programm mit Kommentar, besser klares Programm ohne Kommentar - nicht kommentieren, was ohnehin schon im Programm steht; folgendes ist z.B. unsinnig int sum; // Summe 4 5 Sprache in Kommentaren und Namen Namenswahl für Variablen und Konstanten Deutsch + einfacher Einige Tipps • lesbare aber nicht zu lange Namen z.B. sum, value Englisch + meist kürzer + passt besser zu den englischen Schlüsselwörtern (if, while, ...) + Programm kann international verteilt werden (z.B. über das Web) • Hilfsvariablen, die man nur über kurze Strecken braucht, eher kurz: z.B. i, j, x • Variablen, die man im ganzen Programm braucht, eher länger: z.B. inputText • mit Kleinbuchstaben beginnen, Worttrennung durch Großbuchstaben oder "_" Jedenfalls: Deutsch und Englisch nicht mischen!! z.B. inputText, input_text • Englisch oder Deutsch? 6 7 Zuweisungen Arithmetische Ausdrücke x = y+1 ; Variable Vereinfachte Grammatik 1. berechne den Ausdruck 2. speichere seinen Wert in der Variablen Expr = Operand {BinaryOperator Operand}. Operand = [UnaryOperator] ( identifier | number | "(" Expr ")" ). Ausdruck Binäre Operatoren Bedingung: linke und rechte Seite müssen zuweisungskompatibel sein - müssen dieselben Typen haben, oder - Typ links Typ rechts Hierarchie der ganzzahligen Typen long int short byte Beispiele int i, j; short s; byte b; i = j; // ok: derselbe Typ i = 300; // ok (Zahlkonstanten sind int) b = 300; // falsch: 300 passt nicht in byte i = s; // ok s = i; // falsch + * / % - dass Variablen nur erlaubte Werte enthalten - dass auf Werte nur erlaubte Operationen ausgeführt werden 4/3 = 1 (-4)/3 = -1 4/(-3) = -1 (-4)/(-3) = 1 4%3 = 1 (-4)%3 = -1 4%(-3) = 1 (-4)/(-3) = -1 Unäre Operatoren + - Statische Typenprüfung: Compiler prüft: Addition Subtraktion Multiplikation Division, Ergebnis ganzzahlig Modulo (Divisionsrest) Identität (+x = x) Vorzeichenumkehr 8 9 Typregeln in arithm. Ausdrücken Increment und Decrement Vorrangregeln Variablenzugriff kombiniert mit Addition/Subtraktion - Punktrechnung (*, /, %) vor Strichrechnung (+, -) - Operatoren auf gleicher Stufe werden von links nach rechts ausgewertet (a + b + c) - Unäre Operatoren binden stärker als binäre z.B.: -2 * 4 + 3 ergibt -5 x++ ++x x---x Typregeln Operandentypen Ergebnistyp byte, short, int, long - wenn mindestens 1 Operand long ist - sonst int Beispiele short s; int i; long x; x = x + i; // long i = s + 1; // int (1 ist vom Typ int) s = (short)(s + 1); // Typumwandlung nötig nimmt den Wert von x und erhöht x anschließend um 1 erhöht x um 1 und nimmt anschließend den erhöhten Wert nimmt den Wert von x und erniedrigt x anschließend um 1 erniedrigt x um 1 und nimmt anschließend den erniedrigten Wert Beispiele x = 1; y = x++ * 3; x = 1; y = ++x * 3; long // x = 2, y = 3 // x = 2, y = 6 entspricht: y = x * 3; x = x + 1; entspricht: x = x + 1; y = x * 3; Kann auch als eigenständige Anweisung verwendet werden Typumwandlung (type cast) (type)expression x = 1; x++; - wandelt Typ von expression in type um - dabei kann etwas abgeschnitten werden i // x = 2 entspricht: x = x + 1; Darf nur auf Variablen angewendet werden (nicht auf Ausdrücke) y = (x + 1)++; // falsch! (short)i 10 11 Zuweisungsoperatoren Multiplikation/Division mit Zweierpotenzen Mit Shift-Operationen effizient implementierbar Multiplikation x << 1 x << 2 x << 3 x << 4 ... x*2 x*4 x*8 x * 16 ... Arithmetischen Operationen lassen sich mit Zuweisung kombinieren Division x/2 x/4 x/8 x / 16 ... x >> 1 x >> 2 x >> 3 x >> 4 ... Division nur bei positiven Zahlen durch Shift ersetzbar += -= *= /= %= Beispiele x = 3; x = -3; 0000 0011 x = x << 2; // 12 x = x + y; x = x - y; x = x * y; x = x / y; x = x % y; 0000 1111 x = x << 1; // -6 0000 1100 Langform x += y; x -= y; x *= y; x /= y; x %= y; Spart Schreibarbeit, ist aber nicht schneller als die Langform x = 15; 1111 1101 Kurzform 1111 1010 x = x >> 2; // 3 0000 0011 12 Eingabe und Ausgabe von Werten Eingabe von der Tastatur Besonderheiten zur Eingabe Eingabe von Tastatur Eingabe von einer Datei Eintippen von: Programm Ausgabe auf den Bildschirm Ausgabe http://ssw.jku.at/JavaBuch Return-Taste Programm: // liest eine Zahl vom Eingabestrom // liefert true oder false, je nachdem, ob Lesen erfolgreich war // öffnet Datei als neuen Eingabestrom // schließt Datei und kehrt zum alten Eingabestrom zurück Out.print(x); Out.println(x); Out.open("MyFile.txt"); Out.close(); 12 100 füllt Lesepuffer Ausgabe auf eine Datei Eingabe int x = In.readInt(); if (In.done()) ... In.open("MyFile.txt"); In.close(); 13 // gibt x auf dem Ausgabestrom aus (x kann von bel. Typ sein) // gibt x aus und beginnt eine neue Zeile // öffnet Datei als neuen Ausgabestrom // schließt Datei und kehrt zum alten Ausgabestrom zurück 14 int x = In.readInt(); // liest 12 int y = In.readInt(); // liest 100 int z = In.readInt(); // blockiert, bis Lesepuffer wieder gefüllt ist Ende der Eingabe: Eingabe von Strg-Z in leere Zeile Eingabe von Datei kein Lesepuffer, In.readInt() liest direkt von der Datei Ende der Eingabe wird automatisch erkannt (kein Strg-Z nötig) 15 Grundstruktur von Java-Programmen class ProgramName { public static void main (String[] arg) { ... // Deklarationen ... // Anweisungen } Übersetzen und Ausführen mit JDK Text muss in einer Datei namens ProgramName.java stehen C:\> cd MySamples C:\MySamples> javac Sample.java wechselt ins Verzeichnis mit der Quelldatei erzeugt Datei Sample.class Ausführen C:\MySamples> java Sample Geben Sie 2 Zahlen ein: 3 4 Summe = 7 } Beispiel class Sample { public static void main (String[] arg) { Out.print("Geben Sie 2 Zahlen ein: "); int a = In.readInt(); int b = In.readInt(); Out.print("Summe = "); Out.println(a + b); } } Übersetzen ruft main-Methode der Klasse Sample auf Eingabe mit Return-Taste abschließen Text steht in Datei Sample.java 16 17 If-Anweisung Grundlagen der Programmierung Prof. H. Mössenböck n n>0? j x 3. Verzweigungen ohne else-Zweig if (x > y) max = x; else max = y; mit else-Zweig x/n j max if (n > 0) x = x / n; x x>y? n max y Syntax IfStatement = "if" "(" Expression ")" Statement ["else" Statement]. 2 Anweisungsblöcke Einrückungen Wenn if-Zweig oder else-Zweig aus mehr als 1 Anweisung bestehen, müssen sie durch { ... } geklammert werden. • erhöhen die Lesbarkeit (machen Programmstruktur besser sichtbar) • Einrückungstiefe: 1 Tabulator oder 2 Leerzeichen if (n != 0) x = x / n; Statement = Assignment | IfStatement | Block | ... . Block = "{" {Statement} "}". if (x > y) max = x; else max = y; Beispiel if (x < 0) { negNumbers++; Out.print(-x); } else { posNumbers++; Out.print(x); } if (x < 0) { negNumbers++; Out.print(-x); } else { posNumbers++; Out.print(x); } Kurze If-Anweisungen können auch in einer Zeile geschrieben werden if (n != 0) x = x / n; if (x > y) max = x; else max = y; 3 Dangling Else if (a > b) if (a != 0) max = a; else max = b; 4 Vergleichsoperatoren Vergleich zweier Werte liefert wahr (true) oder falsch (false) if (a > b) if (a != 0) max = a; else max = b; == != > < >= <= Mehrdeutigkeit! Zu welchem if gehört das else? Regel: else gehört immer zum unmittelbar vorausgegangenen if. Wenn man das nicht will, muss man die Anweisung so schreiben: Bedeutung Beispiel gleich ungleich größer kleiner größer oder gleich kleiner oder gleich x == 3 x != y 4>3 x+1 < 0 x <= y x >= y Wird z.B. in If-Anweisung verwendet if (a > b) { if (a != 0) max = a; } else max = b; if (x == 0) Out.println("x is zero"); Achtung: "=" ist in Java kein Vergleich, sondern eine Zuweisung if (x = 0) Out.println("x is zero"); // Compiler meldet einen Fehler! 5 6 Zusammengesetzte Vergleiche && Und-Verknüpfung || Oder-Verknüpfung Kurzschlussauswertung Zusammengesetzter Vergleich wird abgebrochen, sobald Ergebnis feststeht ! Nicht-Verknüpfung x y x && y x y x || y x !x true true false false true false true false true false false false true true false false true false true false true true true false true false false true äquivalent zu if ( a != 0 && b / a > 0 ) x = 0; if (a != 0) if (b / a > 0) x = 0; wenn false, ist gesamter Ausdruck false Beispiel if ( a == 0 || b / a > 0 ) x = 1; if (x >= 0 && x <= 10 || x >= 100 && x <= 110) y = x; wenn true, ist gesamter Ausdruck true if (a == 0) x = 1; else if (b / a > 0) x = 1; Vorrangregeln ! bindet stärker als && && bindet stärker als || Vorrangregeln können durch Klammerung umgangen werden: if (x > 0 && (y == 1 || y == 7)) ... 7 Datentyp boolean Assertionen bei If-Anweisungen nach George Boole: Mathematiker, 1815-1864 Datentyp wie int mit den beiden Werten true und false Beispiele boolean p, q; p = false; q = x > 0; p = p || q && x < 10; if (condition) // condition ... else // ! condition ... diese Assertion sollte man immer hinschreiben oder zumindest im Kopf bilden Beispiel: Maximum dreier Zahlen berechnen int a, b, c, max; a = In.readInt(); b = In.readInt(); c = In.readInt(); if (a > b) /* a>b */ if (a > c) /* a>b && a>c */ max = a; else /* a>b && c>=a */ max = c; else /* b>=a */ if (b > c) /* b>=a && b>c */ max = b; else /* b>=a && c>=b */ max = c; Out.println(max); Beachte • • • • 8 Jeder Vergleich liefert einen Wert vom Typ boolean Boolesche Werte können mit &&, || und ! verknüpft werden Boolesche Werte können in boolean-Variablen abgespeichert werden ("flags") Namen für boolean-Variablen sollten mit Adjektiv beginnen: equal, full 9 10 Programmvergleich Negation zusammengesetzter Ausdrücke Welches der beiden Programme ist besser? if (a > b) if (a > c) max = a; else max = c; else if (b > c) max = b; else max = c; Regeln von DeMorgan max = a; if (b > max) max = b; if (c > max) max = c; ! (a && b) ! (a || b) Augustus De Morgan, britischer Mathematiker, 1806-1871 ! a || ! b ! a && ! b Diese Regeln helfen beim Bilden von Assertionen if (x >= 0 && x < 10) { ... } else { // x < 0 || x >= 10 ... } Was heißt "besser"? • Kürze: das 2. Programm ist kürzer • Effizienz: - 1. Programm braucht immer 2 Vergleiche und 1 Zuweisung - 2. Programm braucht immer 2 Vergleiche und im Schnitt 2 Zuweisungen • Lesbarkeit? • Erweiterbarkeit? 11 Switch-Anweisung Semantik der Switch-Anweisung Switch-Ausdruck Mehrwegverzweigung month 1,3,5,7,8,10,12 days 31 12 4,6,9,11 days 30 2 days switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days = 31; break; case 4: case 6: case 9: case 11: days= 30; break; case 2: days = 28; break; default: Out.println("error"); } sonst 28 error In Java switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days = 31; break; case 4: case 6: case 9: case 11: days= 30; break; case 2: days = 28; break; default: Out.println("error"); } 13 Break-Anweisung • springt ans Ende der Switch-Anweisung • wenn break fehlt, läuft Programm über nächste case-Marke weiter (häufige Fehlerursache!!) Semantik Bedingungen 1. berechne Switch-Ausdruck 2. springe zur passenden case-Marke - wenn keine passt, springe zu default - wenn kein default angegeben, springe ans Ende der Switch-Anweisung 1. switch-Ausdruck muss ganzzahlig oder char sein 2. case-Marken müssen Konstanten sein 3. ihr Typ muss zum Typ des switch-Ausdrucks passen 4. case-Marken müssen voneinander verschieden sein 14 Syntax der Switch-Anweisung Statement Unterschied zwischen If und Switch if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) days = 31; else if (month==4 || month==6 || month==9 || month==11) days = 30; else if (month==2) days = 28; else Out.println("error"); = Assignment | IfStatement | SwitchStatement | ... | Block. SwitchStatement = "switch" "(" Expression ")" "{" {LabelSeq StatementSeq} "}". LabelSeq = Label {Label}. StatementSeq = Statement {Statement}. Label = "case" ConstantExpression ":" | "default" ":". prüft Bedingungen sequentiell benutzt Sprungtabelle 1 days = 31; month 15 switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days = 31; break; case 4: case 6: case 9: case 11: days= 30; break; case 2: days = 28; break; default: Out.println("error"); } 2 3 4 5 6 7 8 9 10 11 12 default days = 28; days = 30; error 16 While-Schleife Führt eine Anweisungsfolge aus, solange eine bestimmte Bedingung gilt Grundlagen der Programmierung i 1 sum 0 Prof. H. Mössenböck i 4. Schleifen sum i n sum + i i+1 i = 1; sum = 0; while ( i <= n ) { sum = sum + i; i = i + 1; } Schleifenbedingung Schleifenrumpf Syntax Statement = Assignment | IfStatement | SwitchStatement | WhileStatement | ... | Block. WhileStatement = "while" "(" Expression ")" Statement . Wenn Schleifenrumpf aus mehreren Anweisungen besteht, muss er mit {...} geklammert werden. 2 Beispiel Assertionen bei Schleifen Aufgabe: Zahlenfolge lesen und Histogramm ausgeben Triviale Assertionen Eingabe: 3 2 5 Aussagen, die sich aus der Schleifenbedingung ergeben Ausgabe: *** ** ***** i = 1; sum = 0; while (i <= n) { /* i <= n */ sum = sum + i; i = i + 1; sollte man immer hinschreiben oder zumindest } im Kopf bilden /* i > n */ class Histogram { public static void main (String[] arg) { int i = In.readInt(); while (In.done()) { ... j = 1; int while (j <= i ) {Out.print("*"); j++;} Out.println(); i = In.readInt(); } } liest die Zahlenfolge Schleifeninvariante gibt i Sterne aus Aussage über das berechnete Ergebnis, die in jedem Schleifendurchlauf gleich bleibt i = 1; sum = 0; while (i <= n) { /* i <= n */ /* sum == Summe(1..i-1) */ sum = sum + i; i = i + 1; } /* i > n */ } 3 4 Verifikation der Schleife Do-While-Schleife "Durchdrücken" der Invariante durch die Anweisungen des Schleifenrumpfs Abbruchbedingung wird am Ende der Schleife geprüft i = 1; sum = 0; while (i <= n) { /* sum == Summe(1..i-1) */ sum = sum + i; /* sum == Summe(1..i) */ i = i + 1; } Beispiel: Ausgabe der Ziffern einer Zahl in umgekehrter Reihenfolge n sum' == sum + i sum == sum' - i == Summe(1..i-1) sum' == Summe(1..i) Out.print(n % 10) n n / 10 j n>0? n i' == i + 1 i == i' - 1 sum == Summe(1..i'-1) int n = In.readInt(); do { Out.print(n % 10); n = n / 10; } while ( n > 0 ); Schreibtischtest n n % 10 123 12 1 0 3 2 1 Syntax /* sum == Summe(1..i-1) */ /* sum == Summe(1..i-1) && i == n+1 In.readInt() Statement = Assignment | IfStatement | WhileStatement | DoWhileStatement | ... | Block. sum == Summe(1..n) */ DoWhileStatement = "do" Statement "while" "(" Expression ")" ";". Termination der Schleife muss auch noch bewiesen werden: i wird in jedem Durchlauf erhöht und ist mit n beschränkt Wenn Schleifenrumpf aus mehreren Anweisungen besteht, muss er mit {...} geklammert werden. 5 6 Do-While-Schleife For-Schleife Warum kann man dieses Beispiel nicht mit einer While-Schleife lösen? Falls die Anzahl der Schleifendurchläufe im voraus bekannt ist int n = In.readInt(); while (n > 0) { Out.print(n % 10); n = n / 10; } "Abweisschleife" sum = 0; for ( i = 1 ; i <= n ; i++ ) sum = sum + i; Kurzform für Weil das für n == 0 die falsche Ausgabe liefern würde. Die Schleife muss mindestens einmal durchlaufen werden, daher : int n = In.readInt(); do { Out.print(n % 10); n = n / 10; } while (n > 0); 1) Initialisierung der Laufvariablen 2) Schleifenbedingung 3) Ändern der Laufvariablen sum = 0; i = 1; while ( i <= n ) { sum = sum + i; i++; } "Durchlaufschleife" 7 Syntax der For-Schleife ForStatement = ForInit = | ForUpdate = 8 Beispiel: Multiplikationstabelle drucken class PrintMulTab { "for" "(" [ForInit] ";" [Expression] ";" [ForUpdate] ")" Statement. Assignment {"," Assignment} Type VarDecl {"," VarDecl}. Assignment {"," Assignment}. public static void main (String[] arg) { int n = In.readInt(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) Out.print(i * j + " "); Out.println(); } } Beispiele for (i = 0; i < n; i++) ... Schreibtischtest für n == 3 i j 1 1 2 3 4 1 2 3 4 1 2 3 4 2 } for (i = 10; i > 0; i--) ... 3 for (int i = 0; i <= n; i = i + 1) ... 1 2 3 for (int i = 0, j = 0; i < n && j < m; i = i + 1, j = j + 2) ... 2 4 6 for (;;) ... 3 6 9 9 1 2 3 2 4 6 3 6 9 4 10 Abbruch von Schleifen Abbruch äußerer Schleifen Beispiel: Summieren mit Fehlerabbruch Beispiel int sum = 0; break verlässt die Schleife, in der int x = In.readInt(); es enthalten ist (while, do, for) while (In.done()) { sum = sum + x; if (sum > 1000) {Out.println("zu gross"); break;} x = In.readInt(); } outer: for (;;) { for (;;) { ... if (...) break; else break outer; ... } } Schleifenabbruch mit break möglichst vermeiden: schwer zu verifizieren. Meist lässt sich dasselbe mit while ebenfalls ausdrücken: // Marke! // Endlosschleife! // verlässt innere Schleife // verlässt äußere Schleife Wann ist ein Schleifenabbruch mit break vertretbar? int sum = 0; int x = In.readInt(); while (In.done() && sum <= 1000) { sum = sum + x; if (sum <= 1000) x = In.readInt(); } // ! In.done() || sum > 1000 if (sum > 1000) Out.println("zu gross"); • bei Abbruch wegen Fehlern • bei mehreren Aussprüngen an verschiedenen Stellen der Schleife • bei echten Endlosschleifen (z.B. in Echtzeitsystemen) 11 Vergleich der Schleifenarten 12 Programm-Muster Wie denken Programmierexperten? E? S Abweisschleife S E? Durchlaufschleife • • S1 E? Muster = Schema zur Lösung häufiger Aufgaben S2 allgemeine Schleife Experten • while (E) S for (I; E; U) S do S while (E) nicht in einzelnen Anweisungen sondern in größeren Programm-Mustern for (;;) { S1; if (E) break; S2; } • benutzen Muster intuitiv - z.B. Stellungen im Schachspiel - z.B. Redewendungen in Geschäftsbriefen lösen Aufgaben in Analogie zu bekannten Aufgaben Frage: Was sind typische Muster in der Programmierung? 13 14 Zusammensetzen der Muster Zusammensetzen von Programmen aus Mustern Beispiel: Berechnung des Mittelwerts einer Zahlenfolge int x = In.readInt(); int sum = 0, n = 0; while (In.done()) { sum = sum + x; n++; x = In.readInt(); } if (n != 0) { float avg = (float)sum / n; Out.println("avg = " + avg); } else Out.println("error"); Einlese-Muster Zähl-Muster int x = In.readInt(); while (In.done()) { ... x = In.readInt(); } int n = 0; while (...) { n++; ... } Summierungs-Muster Prüf-Muster int sum = 0; while (...) { sum = sum + x; ... } if (n != 0) ... else Out.println("error"); Mittelwert-Berechnung Ausgabe float avg = (float)sum / n; Out.println("avg = " + avg); Einlesen Summieren Zählen Prüfen Mittelwert Ausgabe 15 16 Weiteres Beispiel Schrittweise Eingabe von Ablaufstrukturen Und-Verknüpfung von true- und false-Termen Bei Schleifen true true true true false true true false while (In.done()) { } Einlese-Muster Und-Muster boolean b = In.readBoolean(); while (In.done()) { ... b = In.readBoolean(); } boolean ok = true; while (...) { ok = ok && b; ... } boolean b = In.readBoolean(); boolean ok = true; while (In.done()) { ok = ok && b; b = In.readBoolean(); } Out.println(ok); Ausgabe-Muster Out.println(ok); 17 while (In.done()) { x = In.readInt(); } while (In.done()) { ... x = In.readInt(); } Bei Abfragen if (n != 0) { } else { } if (n != 0) { avg = (float)sum / n; Out.println("avg = " + avg); } else { } if (n != 0) { avg = (float)sum / n; Out.println("avg = " + avg); } else { Out.println("error"); } 18 Die Typen float und double Variablen float x, y; double z; Grundlagen der Programmierung Prof. H. Mössenböck // 32 Bit groß // 64 Bit groß Konstanten 3.14 3.14f 3.14E0 0.314E1 31.4E-1 .23 1.E2 5. Gleitkommazahlen // Typ double // Typ float // 3.14 * 100 // 0.314 * 101 // 31.4 * 10-1 // 100 Syntax der Gleitkommakonstanten FloatConstant Digits Exponent FloatSuffix = [Digits] "." [Digits] [Exponent] [FloatSuffix]. = Digit {Digit}. = ("e" | "E") ["+" | "-"] Digits. = "f" | "F" | "d" | "D". 2 Zuweisungen und Operationen Beispiel: Berechnung der harmonischen Reihe sum = 1/1 + 1/2 + 1/3 + ... + 1/n Zuweisungskompatibilität double class HarmonicSequence { long int short byte float f; int i; f = i; // erlaubt i = f; // verboten i = (int)f; // erlaubt: schneidet Nachkommastellen ab; falls zu groß: maxint, minint f = 1.0; // verboten, weil 1.0 vom Typ double ist public static void main (String[] arg) { float sum = 0; int n = In.readInt(); for (int i = n; i > 0; i--) sum += 1.0f / i Out.println("sum = " + sum); } Erlaubte Operationen } • Arithmetische Operationen (+, -, *, /) • Vergleiche (==, !=, <, <=, >, >=) Achtung: Gleitkommazahlen sollte man nicht auf Gleichheit prüfen Was würden statt 1.0f / i folgende Ausdrücke liefern? 1/i 1.0 / i float 0 (weil ganzzahlige Division) einen double-Wert 3 4 Typen von Gleitkommaausdrücken Der "kleinere" Operandentyp wird in den "größeren" konvertiert, zumindest aber in int. double float long int short Grundlagen der Programmierung Prof. H. Mössenböck byte double d; float f; int i; short s; ... d+i // double f+i // float s+s // int 6. Methoden Ein- / Ausgabe von Gleitkommazahlen double d = In.readDouble(); float f = In.readFloat(); Out.println("d = " + d + ", f = " + f); 5 Parameterlose Methoden Wie funktioniert ein Methodenaufruf? Beispiel: Ausgabe einer Überschrift class Sample { static void printHeader() { // Methodenkopf Out.println("Artikelliste"); // Methodenrumpf Out.println("------------"); } public static void main (String[] arg) { printHeader(); // Aufruf ... printHeader(); ... } Zweck von Methoden • Wiederverwendung häufig benutzten Codes static void P() { ... Q(); ... } static void Q() { ... R(); ... } static void R() { ... ... ... } • Definition benutzerspezifischer Operationen • Strukturierung des Programms Namenskonventionen für Methoden Namen sollten mit Verb und Kleinbuchstaben beginnen Beispiele: printHeader, findMaximum, traverseList, ... } 2 3 Parameter Funktionen Werte, die vom Rufer an die Methode übergeben werden Methoden, die einen Ergebniswert an den Rufer zurückliefern class Sample { static void printMax (int x, int y) { if (x > y) Out.print(x); else Out.print(y); } public static void main (String[] arg) { ... printMax(100, 2 * i); } class Sample { formale Parameter - im Methodenkopf (hier x, y) - sind Variablen der Methode static int max (int x, int y) { if (x > y) return x; else return y; } public static void main (String[] arg) { ... int result = 3 * max(100, i + j) + 1; ... } aktuelle Parameter - an der Aufrufstelle (hier 100, 2*i) - können Ausdrücke sein } • haben Funktionstyp (z.B. int) statt void (= kein Typ) • liefern Ergebnis mittels returnAnweisung an den Rufer zurück (x muss zuweisungskompatibel mit int sein) • Werden wie Operanden in einem Ausdruck benutzt } Parameterübergabe Aktuelle Parameter werden den entsprechenen formalen Parametern zugewiesen x = 100; y = 2 * i; aktuelle Parameter müssen mit formalen zuweisungskompatibel sein formale Parameter enthalten Kopien der aktuellen Parameter Funktionen Prozeduren Methoden mit Rückgabewert Methoden ohne Rückgabewert 4 static int max (int x, int y) {...} static void printMax (int x, int y) {...} 5 Weiteres Beispiel Return in Prozeduren Ganzzahliger Zweierlogarithmus class ReturnDemo { static void printLog2 (int n) { if (n <= 0) return; // kehrt zum Rufer zurück int res = 0; while (n > 1) {n = n / 2; res++;} Out.println(res); } class Sample { static int log2 (int n) { // assert: n > 0 int res = 0; while (n > 1) {n = n / 2; res++;} return res; } public static void main (String[] arg) { int x = In.readInt(); if (!In.done()) return; // beendet das Programm printLog2(x); ... } public static void main (String[] arg) { int x = log2(17); // x == 4 ... } } } Funktionen Prozeduren 6 müssen mit return beendet werden können mit return beendet werden 7 Lokale und statische Variablen class C { static int a, b; static void P() { int x, y; ... } ... Beispiel: Summe einer Zahlenfolge Statische Variablen auf Klassenebene mit static deklariert; auch in Methoden dieser Klasse sichtbar falsch! richtig! class Wrong { class Correct { static void add (int x) { int sum = 0; sum = sum + x; } Lokale Variablen in einer Methode deklariert (lokal zu dieser Methode; nur dort sichtbar) static void add (int x) { sum = sum + x; } public static void main(String[] arg) { add(1); add(2); add(3); Out.println("sum = " + sum); } } Reservieren und Freigeben von Speicherplatz Statische Variablen am Programmbeginn angelegt am Programmende wieder freigegeben static int sum = 0; } Lokale Variablen bei jedem Aufruf der Methode neu angelegt am Ende der Methode wieder freigegeben public static void main(String[] arg) { add(1); add(2); add(3); Out.println("sum = " + sum); } } • sum ist in main nicht sichtbar • sum wird bei jedem Aufruf von add neu angelegt (alter Wert geht verloren) 8 Sichtbarkeitsbereich von Namen 9 Beispiel zu Sichtbarkeitsregeln x Programmstück, in dem auf diesen Namen zugegriffen werden kann class Sample { (auch Gültigkeitsbereich oder Scope des Namens genannt) class Sample { Regeln x y static void P() { ... } 1. Ein Name darf in einem Block nicht mehrmals deklariert werden (auch nicht in geschachtelten Anweisungsblöcken). static int x; static int y; 2. Lokale Namen verdecken Namen, die auf Klassenebene deklariert sind. static void Q(int z) { int x; ... } } static void P() { Out.println(x); } z x 3. Der Sichtbarkeitsbereich eines lokalen Namens beginnt bei seiner Deklaration und geht bis zum Ende der Methode. 4. Auf Klassenebene deklarierte Namen sind in allen Methoden der Klasse sichtbar. 10 // gibt 0 aus static int x = 0; x y y' } public static void main(String[] arg) { Out.println(x); // gibt 0 aus int x = 1; // verdeckt statisches x Out.println(x); // gibt 1 aus P(); if (x > 0) { int x; // Fehler: x ist in main bereits deklariert int y; ... } else { int y; // ok, kein Konflikt mit y im then-Zweig ... } for (int i = 0; ...) {...} for (int i = 1; ...) {...} // ok, kein Konflikt mit i aus letzter Schleife } 11 Lebensdauer von Variablen Lokalität class LifenessDemo { Variablen möglichst lokal deklarieren, nicht als statische Variablen. static int g; static void A() { int a; ... } Vorteile • Übersichtlichkeit Deklaration und Benutzung nahe beisammen static void B() { int b; ... A(); ... A(); ... } } • Sicherheit Lokale Variablen können nicht durch andere Methoden zerstört werden public static void main(String[] arg) { int m; ... B(); ... } lokale Variablen (Methodenkeller) statische Var. g • Effizienz Zugriff auf lokale Variable ist oft schneller als auf statische Variable m b m a b m b m a' b m b m m g g g g g g g 12 13 Überladen von Methoden Beispiele Methoden mit gleichem Namen aber verschiedenen Parameterlisten können in derselben Klasse deklariert werden Größter gemeinsamer Teiler nach Euklid static int ggt (int x, int y) { int rest = x % y; while (rest != 0) { x = y; y = rest; rest = x % y; } return y; } static void write (int i) {...} static void write (float f) {...} static void write (int i, int width) {...} Beim Aufruf wird diejenige Methode gewählt, die am besten zu den aktuellen Parametern passt write(100); write(3.14f); write(100, 5); short s = 17; write(s); Kürzen eines Bruchs write (int i) write (float f) write (int i, int width) static void reduce (int z, int n) { int x = ggt(z, n); Out.print(z/x); Out.print("/"); Out.print(n/x); } write (int i); 14 15 Beispiele Beispiele Prüfe, ob x eine Primzahl ist Berechne xn static boolean isPrime (int x) { if (x ==1 || x == 2) return true; if (x % 2 == 0) return false; int i = 3; while (i * i <= x) { if (x % i == 0) return false; i = i + 2; } // i > x und x ist durch keine Zahl // zwischen 1 und i teilbar return true; } static long power (int x, int n) { long res = 1; for (int i = 1; i <= n; i++) res = res * x; return res; } for (int i = 3; i * i <= x; i += 2) if (x % i == 0) return false; Dasselbe effizienter static long power (int x, int n) { long res = 1; while (n > 0) { if (n % 2 == 0) { x = x * x; n = n / 2; // x2n = (x*x)n } else { res = res * x; n--; // xn+1 = x*xn } } return res; } x n res 2 5 4 2 1 1 2 0 32 4 16 16 17 Eindimensionale Arrays Array = Tabelle gleichartiger Elemente Grundlagen der Programmierung Prof. H. Mössenböck 7. Arrays a[0] a[1] a[2] a[3] ... a • • • • Name a bezeichnet das gesamte Array Elemente werden über Indizes angesprochen (z.B. a[3]) Indizierung beginnt bei 0 Elemente sind "namenlose" Variablen Deklaration int[] a; float[] b; • deklariert ein Array namens a (bzw. b) • seine Elemente sind vom Typ int (bzw. float) • seine Länge ist noch unbekannt Erzeugung a = new int[5]; b = new float[10]; a a[0] a[1] a[2] a[3] a[4] • legt ein neues int-Array mit 5 Elementen an (aus dem Heap-Speicher) • weist seine Adresse a zu Array-Variablen enthalten in Java Zeiger auf Arrays! (Zeiger = Speicheradresse) 2 Arbeiten mit Arrays Arrayzuweisung Zugriff auf Arrayelemente • • • • a[3] = 0; a[2*i+1] = a[i] * 3; Arrayelemente werden wie Variablen benutzt Index kann ein ganzzahliger Ausdruck sein Laufzeitfehler, falls Array noch nicht erzeugt wurde Laufzeitfehler, falls Index < 0 oder Arraylänge int[] a, b; a = new int[3]; a a b = a; b int len = a.length; Beispiele // Array einlesen int sum = 0; for (int i = 0; i < a.length; i++) sum += a[i]; // Elemente aufaddieren a[0] a[1] a[2] b bekommt denselben Wert wie a. Arrayzuweisung ist in Java Zeigerzuweisung! 0 0 0 b[0] b[1] b[2] a[0] a[1] a[2] a[0] = 17; a b a = new int[4]; for (int i = 0; i < a.length; i++) a[i] = In.readInt(); Arrayelemente werden in Java standardmäßig mit 0 initialisiert b Arraylänge abfragen • length ist ein Standardoperator, der auf alle Arrays angewendet werden kann. • Liefert Anzahl der Elemente (hier 5). a[0] a[1] a[2] 0 0 0 b = null; ändert in diesem Fall auch b[0] 17 0 0 b[0] b[1] b[2] a 0 0 0 b 17 0 0 a 0 0 0 b 17 0 0 0 0 a zeigt jetzt auf neues Array. null: Spezialwert, der auf kein Objekt zeigt; kann jeder Arrayvariablen zugewiesen werden 3 Freigeben von Arrayspeicher 4 Initialisieren von Arrays Garbage Collection (Automatische Speicherbereinigung) int[] primes = {2, 3, 5, 7, 11}; Objekte, auf die kein Zeiger mehr verweist, werden automatisch eingesammelt. Ihr Speicher steht für neue Objekte zur Verfügung static void P() { int[] a = new int[3]; int[] b = new int[4]; int[] c = new int[2]; ... ... b = a; ... ... c = null; ... ... ... } primes 2 3 5 7 11 identisch zu int[] primes = new int[5]; primes[0] = 2; primes[1] = 3; primes[2] = 5; primes[3] = 7; primes[4] = 11; a b c a b c kein Zeiger mehr auf dieses Objekt wird eingesammelt Initialisierung kann auch bei der Erzeugung erfolgen a b c kein Zeiger mehr auf dieses Objekt wird eingesammelt a b c Am Methodenende werden lokale Variablen freigegeben Zeiger a, b, c fallen weg Objekt wird eingesammelt int[] primes; ... primes = new int[] {2, 3, 5, 7, 11}; 5 6 Kopieren von Arrays int[] a = {1, 2, 3, 4, 5}; int[] b; Kommandozeilenparameter Programmaufruf mit Parametern a 1 2 3 4 5 java Programmname par1 par2 ... parn b Parameter werden als String-Array an main-Methode übergeben b = (int[]) a.clone(); a 1 2 3 4 5 b 1 2 3 4 5 class Sample { public static void main (String[] arg) { for (int i = 0; i < arg.length; i++) Out.println(arg[i]); ... } Typumwandlung nötig, da clone etwas vom Typ Object[] liefert } Aufruf z.B. java Sample Anton /a 10 Ausgabe: Anton /a 10 7 8 Beispiel: sequentielles Suchen Beispiel: binäres Suchen Suchen eines Werts x in einem Array • schneller als sequentielles Suchen • Array muss allerdings sortiert sein falls gefunden 0 17 x pos = 17 falls nicht gefunden 0 99 99 z.B. Suche von 13 pos = -1 static int search (int[] a, int x) { int pos = a.length - 1; while ( pos >= 0 && a[pos] != x ) pos--; // pos == -1 || a[pos] == x return pos; } a 0 2 1 3 2 5 low 3 4 5 6 7 7 11 13 17 19 m high • Index des mittleren Element bestimmen ( m = (low + high) / 2 ) • 13 > a[m] zwischen a[m+1] und a[high] weitersuchen gewünschtes Ergebnis a 0 1 2 2 3 5 4 5 6 7 7 11 13 17 19 3 low m Achtung: int[] a wird nur als Zeiger übergeben. Würde search etwas in a ändern (z.B. a[3] = 0;), würde sich diese Änderung auch auf das Array im Rufer auswirken. 9 high 10 Binäres Suchen Primzahlenberechnung: Sieb des Erathostenes static int binarySearch (int[] a, int x) { int low = 0; int high = a.length - 1; while (low <= high) { int m = (low + high) / 2; if (a[m] == x) return m; else if (x > a[m]) low = m + 1; else /* x < a[m] */ high = m - 1; } /* low > high*/ return -1; } 1. "Sieb" wird mit den natürlichen Zahlen ab 2 gefüllt 2 3 3 5 7 7 8 10 11 15 16 17 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, ... 2 3 3 5 7 7 8 10 11 15 16 17 2. Erste Zahl im Sieb ist Primzahl. Entferne sie und alle ihre Vielfachen 2 3 3 5 7 7 8 10 11 15 16 17 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, ... 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, ... 2 3 3 5 7 7 8 10 11 15 16 17 3. Wiederhole Schritt 2 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, ... 5, 7, 11, 13, 17, 19, 23, 25, ... • Suchraum wird in jedem Schritt halbiert • bei n Arrayelementen sind höchstens log2(n) Schritte nötig, um jedes Element zu finden n seq.Suchen 10 100 1000 10000 ... Wiederhole Schritt 2 bin.Suchen 5, 7, 11, 13, 17, 19, 23, 25, ... 4 7 10 14 10 100 1000 10000 7, 11, 13, 17, 19, 23, ... 11 Implementierung Beispiel: Monatstage berechnen Sieb = boolean-Array, Zahl i im Sieb 3 4 5 6 7 8 9 false false true true true true true true true true 8 9 1 Bisher mit Switch-Anweisung gelöst sieve[i] == true 2 0 switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days = 31; break; case 4: case 6: case 9: case 11: days = 30; break; case 2: days = 28; } ... Zahl i entfernen: sieve[i] = false 0 1 2 3 false false false true 4 12 5 false true 6 7 false true false true ... static void printPrimes (int max) { boolean[] sieve = new boolean[max + 1]; for (int i = 2; i <= max; i++) sieve[i] = true; for (int i = 2; i <= max; ) { Out.print(i + " "); // i is prime for (int j = i; j <= max; j = j + i) sieve[j] = false; while (i <= max && !sieve[i]) i++; } } Besser mit Tabelle int[] days = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; ... int d = days[month]; 13 14 Mehrdimensionale Arrays Mehrdimensionale Arrays Zweidimensionales Array Zeilen können unterschiedlich lang sein (das ist aber selten sinnvoll) 0 1 2 3 0 1 2 a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] a[2][0] a[2][1] a[2][2] a[3][0] a[3][1] a[3][2] Matrix a a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[0] a[1] a[2][2] int[][] a = new int[3][]; a[0] = new int[4]; a[1] = new int[2]; a[2] = new int[3]; a[2] In Java als Array von Arrays implementiert a a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] Initialisierung Deklaration und Erzeugung a[0] a[1] a[2][0] a[2][1] a[2][2] a[3][0] a[3][1] a[3][2] a Zugriff a[2] a[3] int[][] a = {{1, 2, 3},{4, 5, 6}}; int[][] a; a = new int[4][3]; a[i][j] = a[i][j+1]; 1 2 3 4 5 6 15 16 Beispiel: Matrixmultiplikation a i b × k c = i j Grundlagen der Programmierung j Prof. H. Mössenböck c0,0 = a0,0*b0,0 + a0,1*b1,0 + a0,2*b2,0 8. Zeichen static float[][] matrixMult (float[][] a, float[][] b) { float[][] c = new float[a.length][b[0].length]; for (int i = 0; i < a.length; i++) for (int j = 0; j < b[0].length; j++) { float sum = 0; for (int k = 0; k < b.length; k++) sum += a[i][k] * b[k][j]; c[i][j] = sum; } return c; } 17 Datentyp char ASCII char ch = 'x'; Zeichenvariable 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Zeichenkonstante (unter einfachen Hochkommas) Zeichen braucht man zur Verarbeitung von Texten. Zeichencodes ASCII (American Standard Code for Information Interchange) • 1 Zeichen = 1 Byte (128 bzw. 256 Zeichen darstellbar) • z.B. in Pascal oder C verwendet Unicode (www.unicode.org) • 1 Zeichen = 2 Bytes (65536 Zeichen darstellbar) • auch Umlaute, griechische, arabische Zeichen etc. • z.B. in Java und C# verwendet • ASCII ist Teilmenge von Unicode NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 space ! " # $ % & ' ( ) * + , . / 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 @ A B C D E F G H I J K L M N O 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 P Q R S T U V W X Y Z [ \ ] ^ _ 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 ´ a b c d e f g h i j k l m n o 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 Wichtige Steuerzeichen löscht Zeichen vor Cursor BS backspace HT horizontal tab Tabulatorsprung ESC escape CR LF FF carriage return Zeilenvorschub folgt auf CR line feed Seitenvorschub form feed 2 Unicode 0000 - 007F 0080 - 024F 0370 - 03FF 0400 - 04FF 0530 - 058F 0590 - 05FF 0600 - 06FF ... 3 Unicode-Zeichenkonstanten ASCII-Zeichen Umlaute, Akzente, Sonderzeichen griechische Zeichen cyrillische Zeichen armenische Zeichen hebräische Zeichen arabische Zeichen ... Alle Zeichen können auch mit ihrem Unicode-Wert angegeben werden: '\udddd' Beispiele: '\u0041' '\u000d' '\u0009' '\u03c0' Details siehe http://www.unicode.org Deutsche Umlaute 00E4 ä 00F6 ö 00FC ü p q r s t u v w x y z { | } ~ DEL 00C4 Ä 00D6 Ö 00DC Ü 'A' CR (carriage return) TAB Spezielle Zeichen 00DF ß '\n' '\r' '\t' '\\' '\'' '\ddd' 4 LF (line feed, newline) CR (carriage return) TAB \ ' Zeichenwert als Oktalzahl 5 Zeichen-Operationen Beispiel: Textsuche Zuweisungen geg.: Text t, Muster pat ges.: erstes Vorkommen von pat in t char ch1, ch2 = 'a'; ch1 = ch2; // ok, gleicher Typ int i = ch2; // ok, char kann int zugewiesen werden ch1 = (char)i; // Zuweisung nach Typumwandlung möglich double float long int short char Vergleiche (==, !=, <, <=, >, >=) t Einfuehrung in die Programmierung ung byte if ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z') ... Arithmetische Operationen (+, -, *, /, %) // Ergebnistyp: int Zeichenarrays char[] s = new char[20]; char[] t = {'a', 'b', 'c'}; ung Ergebnis: pos 0: pat in t an Stelle pos pos < 0: pat kommt nicht in t vor Zeichen sind nach Unicode-Wert geordnet; Buchstaben und Ziffern liegen aufeinanderfolgend 10 + (ch - 'A') pat ung // initialisiert alle Elemente mit '\u0000' 6 Beispiel: Nachbauen von readInt static int search (char[] t, char[] pat) { int last = t.length - pat.length; for (int i = 0; i <= last; i++) { if (t[i] == pat[0]) { int j = 1; while (j < pat.length && pat[j] == t[i+j]) j++; // j == pat.length || pat[j] != t[i+j] if (j == pat.length) return i; } } return -1; } i t 0 1 2 3 4 5 6 7 8 9 10 pat 0 1 2 j 7 Standardfunktionen mit Zeichen static int readInt() { int val = 0; char ch = In.read(); // liest ein einzelnes Zeichen while (In.done() && '0' <= ch && ch <= '9') { val = 10 * val + (ch - '0'); ch = In.read(); } // ! In.done() || ch < '0' || ch > '9' return val; } if (Character.isLetter(ch)) ... if (Character.isDigit(ch)) ... if (Character.isLetterOrDigit(ch)) ... true, wenn ch ein Unicode-Buchstabe ist true, wenn ch eine Ziffer ist if (Character.isLowerCase(ch)) ... if (Character.isUpperCase(ch)) ... true, wenn ch ein Kleinbuchstabe ist true, wenn ch ein Großbuchstabe ist ch1 = Character.toUpperCase(ch2); ch1 = Character.toLowerCase(ch2); wandelt ch2 in einen Großbuchstaben um wandelt ch2 in einen Kleinbuchstaben um Schreibtischtest: Eingabe 123+ val 0 1 12 123 ch '1' '2' '3' '+' (49) (50) (51) (43) '0' = 48 "Horner-Schema" 8 9 Datentyp String Grundlagen der Programmierung Prof. H. Mössenböck String a, b; Bibliothekstyp für Zeichenketten ("Strings") a = "Hello"; Stringkonstante (unter doppelten Hochkommas) b = a; a b 9. Strings Hello Stringvariablen sind Zeiger auf Stringobjekte Stringzuweisung ist eine Zeigerzuweisung Stringobjekte sind nicht als Arrays ansprechbar Stringobjekte sind nicht veränderbar b = a + " World"; a Hello b Hello World Verkettung mit "+" erzeugt neues Stringobjekt (relativ teure Operation) 2 Stringvergleiche Stringoperationen String s = "a long string"; String s = In.readWord(); // liest ein Wort, z.B. Hello if (s == "Hello") ... // liefert false! (Zeigervergleich) s Hello Hello if (s.equals("Hello")) ... weil zwei verschiedene Objekte, trotz gleichem Inhalt // liefert true! (Wertvergleich) aber: String s = "Hello"; if (s == "Hello") .. // liefert true, weil gleichlautende Stringkonstanten // nur einmal als Objekt abgespeichert werden. trotzdem Wertvergleich immer mit equals durchführen 3 0 1 2 3 4 a l o n g 5 6 7 8 9 10 11 12 S t r i n g int len = s.length(); liefert Anzahl der Zeichen in s (im Gegensatz zu arr.length sind Klammern nötig!) char ch = s.charAt(3); liefert das Zeichen mit Index 3 (hier 'o') int i = s.indexOf("ng"); i = s.indexOf("ng", 5); i = s.indexOf('n'); i = s.lastIndexOf("ng"); liefert Index des 1. Vorkommens von "ng" in s (hier 4) oder -1 liefert Index des 1. Vorkommens von "ng" ab Index 5 (hier 11) geht auch mit char liefert Index des letzten Vorkommens von "ng" (Varianten wie oben) String x; x = s.substring(2); x = s.substring(2, 6); liefert Teilstring ab Index 2 (hier "long string") liefert Teilstring s[2..6[, d.h. s[2..5] (hier "long") if (s.startsWith("abc")) ... if (s.endsWith("abc")) ... liefert true, falls s mit "abc" beginnt liefert true, falls s mit "abc" ended 4 Aufbauen von Strings Aufbauen von Strings aus StringBuilder aus Stringkonstante aus StringBuilder (Bibliothekstyp wie String, aber modifizierbar) String s = "very simple"; StringBuilder b; b = new StringBuilder(); // erzeugt leeren StringBuilder der Länge 0 aus char-Array char[] a = {'a', 'b', 'c', 'd', 'e'}; len = b.length(); b.append(x); String s1 = new String(a); // s1 enthält Kopie der Zeichen in a String s2 = new String(a, 2, len); // s2 enthält Kopie von a[2..2+len-1] b.insert(pos, x); b.delete(from, to); b.replace(from, to, "abc"); // hängt x an b an. x kann beliebigen Typ haben: // short, int, long, float, double, char, char[], String, boolean // fügt x an der Stelle pos ein (Typ von x beliebig) // löscht [from..to[ aus b // ersetzt b[from, to[ durch "abc" ch = b.charAt(i); s = b.substring(from, to); // wie bei String b.setCharAt(pos, 'x'); // setzt b[pos] auf 'x' s = b.toString(); // liefert Pufferinhalt als String 5 Stringkonversionen 6 Beispiel: Manipulation von Dateipfaden int i = new Integer("123").intValue(); float f = new Float("3.14").floatValue(); // String // String String s; s = String.valueOf(123); s = String.valueOf(3.14); // int String // float String char[] a = s.toCharArray(); String s = new String(a); // String // char[] dir1\dir2\name.java int float name.class - Verzeichnisse entfernen - ".java" auf ".class" ändern (bzw. ".class" anhängen) static String strip (String path) { StringBuilder b = new StringBuilder(path); // erzeugt StringBuilder mit path als Inhalt if (path.endsWith(".java")) { int len = path.length(); b.delete(len-5, len); } b.append(".class"); int i = path.lastIndexOf('\\'); if (i >= 0) b.delete(0, i+1); return b.toString(); } char[] String 7 8 Beispiel: Wörter aus einem Text lösen Beispiel: Zahl in String konvertieren Eingabe: "Ein Text aus Woertern ..." Ausgabe: Ein Text aus Woertern Idee: Ziffern mit n % 10 abspalten und in char-Array sammeln static void printWords (String text) { int i = 0, last = text.length() - 1; while (i <= last) { //--- skip nonletters while (i <= last && !Character.isLetter(text.charAt(i))) i++; // end of text or text[i] is a letter //--- read word int beg = i; while (i <= last && Character.isLetter(text.charAt(i))) i++; // end of text or text[i] is not a letter //--- print word if (i > beg) Out.println(text.substring(beg, i)); } } a n 0 1 2 a b c i 3 4 5 static String valueOf (int n) { char[] a = new char[20]; int i = 0; do { a[i] = (char) (n % 10 + '0'); n = n / 10; i++; } while (n > 0); StringBuilder b = new StringBuilder(); do { i--; b.append(a[i]); } while (i > 0); return b.toString(); } 6 d e f last 154 15 i "4" 1 i "4" "5" 0 i "4" "5" "1" i "4" "5" "1" i "4" "5" i "4" i b "1" "15" "154" 9 10 Motivation Wie würde man ein Datum speichern (z.B. 13. November 2004)? Grundlagen der Programmierung Prof. H. Mössenböck 10. Klassen 3 Variablen int day; String month; int year; Unbequem, wenn man mehrere Exemplare davon braucht: int day1; String month1; int year1; int day2; String month2; int year2; ... Idee: die 3 Variablen zu einem eigenen Datentyp zusammenfassen 2 Datentyp Klasse Objekte Speicherung verschiedenartiger Werte unter einem gemeinsamen Namen Objekte einer Klasse müssen vor ihrer ersten Benutzung erzeugt werden Deklaration Date x, y; class Date { int day; String month; int year; } reserviert nur Speicher für die Zeigervariablen x Felder der Klasse Date y Erzeugung x = new Date(); erzeugt ein Date-Objekt und weist seine Adresse x zu x Verwendung als Typ 0 null 0 y Date x, y; x Zugriff Date-Objekt neu erzeugte Objekte werden mit 0, null, false, '\u0000' initialisiert Eine Klasse ist wie eine Schablone, von der beliebig viele Objekte erzeugt werden können. 13 day "November" month 2004 year x.day = 13; x.month = "November"; x.year = 2004; day month year Benutzung x.day = 13; x.month = "November"; x.year = 2004; Date-Variablen sind Zeiger auf Objekte x y 13 "November" 2004 day month year 3 Zuweisungen y = x; x y y.day = 20; x y Vergleiche 13 "November" 2004 day month year Zeigerzuweisung! 20 "November" 2004 day month year ändert auch x.day! Zeigervergleich Zuweisungen sind erlaubt, wenn die Typen gleich sind class Date { int day; String month; int year; } d1 = d2; a1 = a2; d1 = a2; 4 class Address { int number; String street; int zipCode; } x == y x != y vergleicht nur Zeiger x<y x <= y x>y x >= y nicht erlaubt Wertvergleich muss mittels Vergleichsmethode selbst implementiert werden Date d1, d2 = new Date(); Address a1, a2 = new Address(); static boolean equalDate (Date x, y) { return x.day == y.day && x.month.equals(y.month) && x.year == y.year; } // ok, gleiche Typen // ok, gleiche Typen // verboten: verschiedene Typen trotz gleicher Struktur! 5 6 Wo werden Klassen deklariert In einer einzigen Datei (auf äußerster Ebene) Beispiel: Polygone In getrennten Dateien MainProgram.java class Point { int x, y; } C1.java class C1 { ... } class C1 { ... } class C2 { ... } class C2 { ... } } class MainProgram { Polygon p pt color public static void main (String[] arg) { ... } 20 Point 20 40 Point p = new Point(); p.x = 60; p.y = 40; poly.pt[2] = p; } p = new Point(); p.x = 70; p.y = 20; poly.pt[3] = p; ... Zugriff z.B. 60 poly.pt[0].x = ...; 40 Point 70 20 javac MainProgram.java C1.java C2.java 7 8 Kombination von Klassen mit Arrays Methoden mit mehreren Rückgabewerten Beispiel: Telefonbuch Java-Funktionen haben nur 1 Rückgabewert Will man mehrere Rückgabewerte, muss man sie zu einer Klasse zusammenfassen Beispiel: Umrechnung von Sekunden auf Std, Min, Sek Point 10 p = new Point(); p.x = 20; p.y = 40; poly.pt[1] = p; Übersetzung javac MainProgram.java poly (70,20) Point p = new Point(); p.x = 10; p.y = 20; poly.pt[0] = p; MainProgram.java public static void main (String[] arg) { ... } (60,40) (10,20) ... Polygon poly = new Polygon(); poly.pt = new Point[4]; poly.color = RED; C2.java class MainProgram { (20,40) class Polygon { Point[] pt; int color; } sec 0 h m s convert class Time { int h, m, s; } Time name Maier Mayr Meier phone 876 8878 543 2343 656 2332 zweidimensionales Array kann hier nicht verwendet werden 99 Objekt bestehend aus 2 Arrays Array von Objekten class Person { String name; int phone; } Person[] book = new Person[100]; class Program { static Time convert (int sec) { Time t = new Time(); t.h = sec / 3600; t.m = (sec % 3600) / 60; t.s = sec % 60; return t; } public static void main (String[] arg) { Time t = convert(10000); Out.println(t.h + ":" + t.m + ":" + t.s); } } class PhoneBook { String[] name; int[] phone; } PhoneBook book = new PhoneBook(); book.name = new String[100]; book.phone = new int[100]; book book Maier 876 8878 name phone Maier 9 Diese Lösung ist aus logischer Sicht besser 876 8878 10 Implementierung Implementierung (Fortsetzung) class Person { String name; int phone; } class PhoneBookSample { static Person[] book; static int nEntries = 0; // current number of entries in book static void enter (String name, int phone) { if (nEntries >= book.length) Out.println("--- phone book full"); else { book[nEntries] = new Person(); book[nEntries].name = name; book[nEntries].phone = phone; nEntries++; } } static int lookup (String name) { int i = 0; while (i < nEntries && !name.equals(book[i].name)) i++; // i >= nEntries || name.equals(book[i].name) if (i < nEntries) return book[i].phone; else return -1; } ... public static void main (String[] arg) { book = new Person[1000]; //----- read the phone book from a file In.open("phonebook.txt"); String name = In.readName(); int phone; while (In.done()) { phone = In.readInt(); enter(name, phone); name = In.readName(); } In.close(); //----- search in the phone book for (;;) { Out.print("Name: "); name = In.readName(); if (!In.done()) break; phone = lookup(name); if (phone >= 0) Out.println("phone number = " + phone); else Out.println(name + " unknown"); } } } // end PhoneBookSample book 0 Maier 876 8878 1 2 3 nEntries = 3 11 12 Klasse = Daten + Methoden Beispiel: Positionsklasse Grundlagen der Programmierung class Position { private int x; private int y; Prof. H. Mössenböck void goLeft() void goRight() void goUp() void goDown() 11. Objektorientierung { x = x - 1; } { x = x + 1; } { y = y - 1; } { y = y + 1; } } Methoden sind • lokal zu Position • ohne static deklariert (siehe später) Black-Box Position pos = new Position(); pos.goRight(); // pos.x == 1, pos.y == 0 pos.goDown(); // pos.x == 1, pos.y == 1 pos.goDown(); // pos.x == 1, pos.y == 2 ... ruft goDown-Methode von pos auf Jedes Objekt hat seinen eigenen Zustand Position pos2 = new Position(); pos2.goUp(); // pos2.x == 0, pos2.y == -1 pos2.goLeft(); // pos2.x == -1, pos2.y == -1 ... pos.goRight(); goLeft goRight goUp goDown x Benutzung y Man sagt: • pos bekommt die Nachricht (message) goRight • pos ist der Empfänger der Nachricht goRight 2 Schlüsselwort this Beispiel: Bruchzahlenklasse Methoden können Parameter haben class Fraction { Position pos = new Position(); pos.goLeft(3); // pos.x == -3, pos.y == 0 ... class Position { private int x; private int y; int z; // Zähler int n; // Nenner void mult (Fraction f) { z = z * f.z; n = n * f.n; } void goLeft(int n) { x = x - n; } ... } void add (Fraction f) { z = z * f.n + f.z * n; n = n * f.n; } Schlüsselwort this class Position { private int x; private int y; Fraction a = new Fraction(); a.z = 1; a.n = 2; Fraction b = new Fraction(); b.z = 3; b.n = 5; a 1 2 z n b 3 5 a a.mult(b); a b.mult(a); z n 3 z 10 n 1 2 z n b b 3 5 z n 3 10 z n } void goLeft(int x) { this.x = this.x - x; } ... } this bezeichnet das Objekt, auf das goLeft angewendet wird (nötig, um Feld x vom Parameter x zu unterscheiden) Es wird immer der Zustand des Empfängers verändert! 3 4 Grafische Notation für Klassen Konstruktoren UML-Notation (Unified Modeling Language) Spezielle Methoden, die beim Erzeugen eines Objekts automatisch aufgerufen werden Fraction Klassenname int z int n Felder void mult (Fraction f) void add (Fraction f) Methoden class Fraction { int z, n; Fraction (int z, int n) { this.z = z; this.n = n; } dienen zur Initialisierung eines Objekts heißen wie die Klasse ohne Funktionstyp und ohne void können Parameter haben können überladen werden Fraction () { z = 0; n = 1; } Vereinfachte Form void mult (Fraction f) {...} void add (Fraction f) {...} } Fraction z n • • • • • falls weniger Details gewünscht oder nötig Aufruf Fraction f = new Fraction(); Fraction g = new Fraction(3, 5); mult(f) add(f) 5 1. legt neues Fraction-Objekt an 2. ruft für dieses Objekt den Konstruktor auf 6 Bsp: Polygon-Aufbau mit Konstruktoren class Point { int x, y; Point (int x, int y) { this.x = x; this.y = y; } } class Polygon { Point[] pt; int color; Polygon (Point[] pt, int color) { this.pt = pt; this.color = color; } } (20,40) (10,20) static class Window { int x, y, w, h; static int border; (60,40) (70,20) // Objektfelder (in jedem Window-Objekt vorhanden) // Klassenfeld (nur einmal pro Klasse vorhanden) void redraw () {...} // Objektmethode (auf Objekte anwendbar) static void setBorder (int n) {border = n;} // Klassenmethode (auf Klasse Window anwendbar) poly class Program { ... Polygon poly = new Polygon( new Point[] { new Point(10, 20), new Point(20, 40), new Point(60, 40), new Point(70, 20) }, RED ); ... } Polygon Point pt color 10 Window(int w, int h) {...} static { ... } // Objektkonstruktor (zur Initialisierung von Objekten) // Klassenkonstruktor (zur Initialisierung der Klasse) } 20 Klasse Window 20 border 40 setBorder() Klassenkonstruktor 60 Window-Objekt x y w h redraw() Window() 40 70 Window-Objekt x y w h redraw() Window() Window-Objekt x y w h redraw() Window() • Objektmethoden haben Zugriff auf Klassenfelder (redraw kann auf border zugreifen) • Klassenmethoden haben keinen direkten Zugriff auf Objektfelder (setBorder kann nicht auf x zugreifen) 20 7 static (Forts.) static (Forts.) Was geschieht wann? class Window { Beim Laden der Klasse Window - Klassenfelder werden angelegt (border) - Klassenkonstruktor wird aufgerufen 8 statische Programmelemente static int border; angelegt am Programmanfang (wenn die Klasse geladen wird) static {...} aufgerufen am Programmanfang (wenn die Klasse geladen wird) static void setBorder(int n) {...} Beim Erzeugen eines Window-Objekts (new Window(...)) - Objektfelder werden angelegt (x, y, w, h) - Objektkonstruktor wird aufgerufen nichtstatische Programmelemente int x, y, w, h; angelegt, wenn das Window-Objekt erzeugt wird Window() {...} aufgerufen, wenn das Window-Objekt erzeugt wird void redraw() {...} void foo() { x = 0; redraw(); border = 1; setBorder(1); } Zugriffe Zugriff auf static-Elemente über den Klassennamen - Window.border = ...; Window.setBorder(3); - Methoden der Klasse Window können Klassennamen weglassen (border = ...; setBorder(3);) Zugriff auf nonstatic-Elemente über einen Objektnamen - Window win = new Window(100, 50); win.x = ...; win.redraw(); - Methoden der Klasse Window können auf eigene Elemente direkt zugreifen (x = ...; redraw();) 9 Zugriff auf eigene Elemente ohne Qualifikation } ... Window w = new Window(); w.x = 0; w.redraw(); Window.border = 1; Window.setBorder(1); ... Zugriff auf fremde Elemente mit Qualifikation Statische Felder leben während der gesamten Programmausführung! 10 Beispiel: Stack und Queue Klasse Stack Stack (Stapel, Kellerspeicher) push(x); x = pop(); class Stack { int[] data; int top; fügt x hinten an den Stack an entfernt und liefert hinterstes Stackelement push(3); 3 push(4); 3 x = pop(); 3 // y == 3 Queue (Puffer, Schlange) put(x); x = get(); fügt x hinten an die Queue an entfernt und liefert vorderstes Queueelement put(3); 3 put(4); 3 x = get(); y = get(); 4 int pop () { if (top < 0) { Out.println("-- underflow"); return 0; } else return data[top--]; } FIFO-Datenstruktur (first in first out) 4 top void push (int x) { if (top == data.length - 1) Out.println("-- overflow"); else data[++top] = x; } // x == 4 y = pop(); data Stack (int size) { data = new int[size]; top = -1; } LIFO-Datenstruktur (last in first out) 4 0 // x == 3 } // y == 4 Benutzung Stack s = new Stack(10); s.push(3); s.push(6); int x = s.pop() + s.pop(); // x == 9 11 Klasse Queue Klassifikation class Queue { int[] data; int head, tail, n; Queue (int size) { data = new int[size]; head = 0; tail = 0; n = 0; } void put (int x) { if (n == data.length) Out.println("-- overflow"); else { data[tail] = x; n++; tail = (tail+1) % data.length; } } } int get () { if (n == 0) { Out.println("-- underflow"); return 0; } else int x = data[head]; n--; head = (head+1) % data.length; return x; } 12 Dinge der realen Welt lassen sich oft klassifizieren z.B. Artikel eines Web-Shops Artikel data head tail n == 3 Buch Audio Kamera ... data tail head n == 4 HardCover SoftCover eBook CD Cassette Digital Analog Man beachte Benutzung Vererbung • Ein eBook hat alle Eigenschaften eines Buchs; zusätzlich hat es ... Ein Buch hat alle Eigenschaften eines Artikels; zusätzlich hat es ... Queue q = new Queue(10); q.put(3); q.put(6); int x = q.get(); // x == 3 int y = q.get(); // y == 6 • CD und Cassette lassen sich gleichermaßen als Audio behandeln Buch, Audio und Kamera lassen sich gleichermaßen als Artikel behandeln 13 14 Vererbung Überschreiben von Methoden class Article { int code; int price; boolean available() {...} void print() {...} Article(int c, int p) {...} } Oberklasse Basisklasse class Book extends Article { String author; String title; void print() {...} Book(int c, int p, String a, String t) {...} } Unterklasse Article code price available() print() Article(c, p) class Article { ... void print() { Out.print(code + " " + price); } } Book author title erbt: code, price, available, print ergänzt: author, title, Konstruktor überschreibt: print print() Book(c, p, a, t) } Book book = new Book(code, price, author, title); erzeugt Book-Objekt Book-Konstruktor Article-Konstruktor (code = c; price = p;) author = a; title = t; Article(int c, int p) { code = c; price = p; } book class Book extends Article { ... void print() { super.print(); Out.print(" " + author + ": " + title); } Wenn keine Oberklasse angegeben wird, ist sie Object Benutzung Book(int c, int p, String a, String t) { super(c, p); author = a; title = t; } code price author title book.print(); print aus Book print aus Article Out.print(...); Ausgabe: code price author: title 15 Klassenhierarchien 16 Kompatibilität zwischen Klassen Article Unterklassen sind Spezialisierungen ihrer Oberklassen code price Book-Objekte können Article-Variablen zugewiesen werden available() print() Article a = new Book(code, price, author, title); a Book Audio Camera author title songs supplier print() print() print() ... CD Cassette tracks lengh print() print() Jedes Buch ist ein Artikel Aber: nicht jeder Artikel ist ein Buch code price author: title code price author title nur Article-Felder sind über a zugreifbar a.code a.price if (a instanceof Book) Book b = (Book) a; ... b 17 code price author title // Laufzeittyptest // Typumwandlung mit Laufzeittypprüfung alle Book-Felder sind über b zugreifbar b.code b.price b.author b.title 18 Dynamische Bindung Heterogene Datenstruktur Article Article[] a; Grundlagen der Programmierung available() print() Book CD Book Camera CD Alle Varianten können als Artikel behandelt werden void printArticles() { for (int i = 0; i < a.length; i++) { if (a[i].available()) { a[i].print(); } } } Prof. H. Mössenböck Book Audio Camera print() print() print() 12. Dynamische Datenstrukturen ruft available() aus Article auf ruft je nach Artikelart das print() aus Book, CD oder Camera auf Dynamische Bindung obj.print() ruft die print-Methode des Objekts auf, auf das obj gerade zeigt 19 Warum "dynamisch" Verknüpfen von Knoten • Elemente werden zur Laufzeit (dynamisch) mit new angelegt • Datenstruktur kann dynamisch wachsen und schrumpfen class Node { int val; Node next; Node(int v) {val = v;} } Wichtigste dynamische Datenstrukturen Erzeugen a Node a = new Node(3); Node b = new Node(5); Verknüpfen Liste Baum Graph a.next = b; b 3 a 5 b 3 5 Bestehen aus "Knoten", die über "Kanten" miteinander verbunden sind. Knoten ... Objekte Kanten ... Zeiger 2 3 Unsortierte Liste Unsortierte Liste (Forts.) Einfügen am Listenende Einfacher ist es, am Listenanfang einzufügen class List { Node head = null, tail = null; void append (int val) { // Einfügen am Listenende Node p = new Node(val); if (head == null) head = p; else tail.next = p; tail = p; } ... } head tail 8 3 head 7 class List { Node head = null; void prepend (int val) { // Einfügen am Listenanfang Node p = new Node(val); p.next = head; head = p; } ... } tail val head p p 8 3 7 head tail val head val val p p 4 5 Unsortierte Liste (Forts.) head 8 class List { Node head = null; ... p Node search (int val) { // Suchen eines Werts Node p = head; while (p != null && p.val != val) p = p.next; // p == null || p.val == val return p; } void delete (int val) { // Löschen eines Werts Node p = head, prev = null; while (p != null && p.val != val) { prev = p; p = p.next; } // p == null || p.val == val if (p != null) // p.val == val head if (p == head) head = p.next 8 else prev.next = p.next; } } prev prev p } 3 p 7 Suchen von 7 Grundlagen der Programmierung p Prof. H. Mössenböck 13. Rekursion 3 7 Löschen von 3 p 6 Was heißt "rekursiv" Ablauf einer rekursiven Methode n=4 24 long fact (long n) { if (n == 1) return 1; else return fact(n-1) * n; } Eine Methode m() heißt rekursiv, wenn sie sich selbst aufruft m() m() m() n() m() direkt rekursiv indirekt rekursiv n=3 6 long fact (long n) { if (n == 1) return 1; else return fact(n-1) * n; } Beispiel: Berechnung der Fakultät (n!) rekursive Definition n! = 1 * 2 * 3 * ... * (n-1) * n n! = (n-1)! * n 1! = 1 (n-1)! Rekursive Methode zur Berechnung der Fakultät 4! = 3! * 4 3! = 2! * 3 2! = 1! * 2 1! = 1 2 n=2 long fact (long n) { if (n == 1) return 1; else return fact(n-1) * n; } Allgemeines Muster long fact (long n) { if (n == 1) return 1; else return fact(n-1) * n; } Jede Aktivierung von fact hat ihr eigenes n und rettet es über den rekursiven Aufruf hinweg 1 n=1 long fact (long n) { if (n == 1) return 1; else return fact(n-1) * n; } if (Problem klein genug) nichtrekursiver Zweig; else rekursiver Zweig mit kleinerem Problem 2 Beispiel: binäres Suchen rekursiv Ablauf des rekursiven binären Suchens z.B. Suche von 17 (Array muss sortiert sein) a 0 2 1 3 2 5 low 0 a 2 3 4 5 6 7 7 11 13 17 19 m 1 3 2 5 • Index m des mittleren Element bestimmen • 17 > a[m] in rechter Hälfte weitersuchen high 4 5 6 7 7 11 13 17 19 rekursiver Zweig 4 low 3 4 5 6 7 7 11 13 17 19 m high 6 low = 6, high = 7 nichtrekursiver Zweig 0 1 2 2 3 5 m=3 static int search (int elem, int[] a, int low, int high) { if (low > high) return -1; int m = (low + high) / 2; if (elem == a[m]) return m; if (elem < a[m]) return search(elem, a, low, m-1); return search(elem, a, m+1, high); } high static int search (int elem, int[] a, int low, int high) { if (low > high) return -1; // empty int m = (low + high) / 2; if (elem == a[m]) return m; if (elem < a[m]) return search(elem, a, low, m-1); return search(elem, a, m+1, high); } 6 elem = 17, low = 0, high = 7 static int search (int elem, int[] a, int low, int high) { if (low > high) return -1; int m = (low + high) / 2; if (elem == a[m]) return m; if (elem < a[m]) return search(elem, a, low, m-1); return search(elem, a, m+1, high); } low = 4, high = 7 3 low m 3 0 1 2 2 3 5 m=5 3 4 5 6 7 7 11 13 17 19 low m high 6 static int search (int elem, int[] a, int low, int high) { if (low > high) return -1; int m = (low + high) / 2; if (elem == a[m]) return m; if (elem < a[m]) return search(elem, a, low, m-1); return search(elem, a, m+1, high); } m=6 0 1 2 3 4 5 6 2 3 5 7 11 13 17 19 7 low high m 5 Beispiel: größter gemeinsamer Teiler rekursiv static int ggt (int x, int y) { int rest = x % y; if (rest == 0) return y; else return ggt(y, rest); } iterativ static int ggt (int x, int y) { int rest = x % y; while (rest != 0){ x = y; y = rest; rest = x % y; } return y; } Grundlagen der Programmierung Prof. H. Mössenböck 14. Schrittweise Verfeinerung Jeder rekursive Algorithmus kann auch iterativ programmiert werden • rekursiv: meist kürzerer Quellcode • iterativ: meist kürzere Laufzeit Rekursion v.a. bei rekursiven Datenstrukturen nützlich (Bäume, Graphen, ...) 6 Entwurfsmethode für Algorithmen Vorgehensweise Schrittweise Verfeinerung Wie kommt man von der Aufgabenstellung zum Programm? 1. Zerlege Aufgabe in Teilaufgaben und spezifiziere deren Schnittstelle Beispiel 2. Nimm an, dass die Teilaufgaben schon gelöst sind 3. Implementiere Gesamtaufgabe mit Hilfe der Teillösungen 4. Sind die Teilaufgaben einfach genug? ja => implementiere sie direkt in einer Programmiersprache nein => Zerlege sie weiter (Schritt 1) geg.: Text aus Wörtern ges.: Wie oft kommt jedes Wort im Text vor? Welche Befehle würde man sich wünschen, um diese Aufgabe zu lösen? • • • • lies Wort speichere und suche Wort in einer Tabelle erhöhe Wortzähler für gespeichertes Wort drucke Zähler für alle gespeicherten Worte Was bringt das? Komplexität tatsächlich c zu erwarten Leider gibt es keine Sprache mit diesen Befehlen Befehle als Methoden implementieren (d.h. gewünschte "Sprache" selbst bauen) < c/2 2 Programmgröße n/2 n Komplexität steigt überproportional mit der Programmgröße Halbierung der Programmgröße reduziert die Komplexität um mehr als die Hälfte! 3 Beispiel Beispiel (Forts.) Aufgabe: "Zähle die Häufigkeit von Wörtern in einem Text" 3. Implementiere Gesamtaufgabe mit Hilfe der Teillösungen 1. Zerlege Aufgabe in Teilaufgaben und spezifiziere deren Schnittstelle word = readWord(); class WordCount { public static void main (String[] arg) { WordTable tab = new WordTable(); In.open("input.txt"); String word = readWord(); while (word != null) { tab.count(word); word = readWord(); } In.close(); tab.print(); } liefert nächstes Wort oder null, wenn kein Wort mehr von der aktuellen Eingabedatei gelesen werden kann. Klasse WordTable mit folgenden Methoden: tab.count(word); trägt word in tab ein (falls noch nicht vorhanden) und erhöht Worthäufigkeit um 1 tab.print(); gibt Wörter und ihre Häufigkeiten aus 2. Nimm an, dass die Teilaufgaben schon gelöst sind } Was haben wir bisher geleistet? 4 Sehr viel! Wir haben die komplexe Aufgabe WordCount auf relativ einfache Teilaufgaben wie readWord() oder tab.count(word) reduziert. 5 Beispiel (Forts.) Beispiel (Forts.) 4. Zerlege Teilaufgaben weiter (readWord) Grobstruktur der Worttabelle ch = In.read(); Character.isLetter(ch) word.append(ch) lies ein Zeichen; In.done == false, wenn Dateiende prüfe, ob ch ein Buchstabe ist füge ch an das Wort word an Datenstruktur: verkettete Liste von Wörtern und Häufigkeiten first word freq next Alle Teilaufgaben sind bereits in der Java-Bibliothek implementiert. Implementiere readWord() mit ihnen static String readWord () { StringBuilder word = new StringBuilder(); char ch; //----- skip nonletters do ch = In.read(); while (In.done() && ! Character.isLetter(ch)); // !In.done() || ch is a letter //----- build the word while (In.done() && Character.isLetter(ch)) { word.append(ch); ch = In.read(); } // ! In.done() || ch is not a letter if (word.length > 0) return word.toString(); else return null; } class Element { String word; int freq; Element next; Element (String w) { word = w; freq = 1; } class WordTable { Element first = null; void count (String word) {...} void print () {...} } } 6 7 Beispiel (Forts.) Beispiel (Forts.) 4. Zerlege Teilaufgaben weiter (tab.count(word)) 4. Zerlege Teilaufgaben weiter (tab.print()) Ist so einfach, dass man es sofort implementieren kann elem = tab.find(word); tab.enter(word); freq++; suche word in tab und liefere entspr. Element oder null trage word in tab ein (es kommt noch nicht vor) erhöhe Worthäufigkeit void print () { for (Element e = first; e != null; e = e.next) Out.println(e.word + ": " + e.freq); } void count (String word) { Element e = tab.find(word); if (e == null) tab.enter(word); else e.freq++; } find und enter sind so einfach, dass man sie sofort implementieren kann Element find (String word) { Element e = first; while (e != null && !word.equals(e.word)) { e = e.next; } // e == null || word.equals(e.word) return e; } void enter (String word) { Element e = new Element(word); e.next = first; first = e; } 8 Zusammensetzen der einzelnen Teile class Element { String word; int freq; Element next; } 9 Zusammensetzen der einzelnen Teile class WordCount { class WordTable { private Element first = null; public static void main (String[] arg) { WordTable tab = new WordTable(); In.open("input.txt"); String w = readWord(); while (w != null) {tab.count(w); w = readWord();} In.close(); tab.print(); } private Element find (String word) { Element e = first; while (e != null && !word.equals(e.word)) e = e.next; return e; } Element (String w) { word = w; freq = 1; } private void enter (String word) { Element e = new Element(word); e.next = first; first = e; } static String readWord () { StringBuilder word = new StringBuilder(); char ch; do ch = In.read(); while (In.done() && ! Character.isLetter(ch)); while (In.done() && Character.isLetter(ch)) { word.append(ch); ch = In.read(); } if (word.length > 0) return word.toString(); else return null; } void count (String word) { Element e = tab.find(word); if (e == null) tab.enter(word); else e.freq++; } } void print () { for (Element e = first; e != null; e = e.next) Out.println(e.word + ": " + e.freq); } } 10 11 Aufrufhierarchie Weiteres Beispiel: Index erzeugen Eingabe Seitennummer WordCount WordTable read isLetter count find Ende einer Seitenangabe 1 = if-Anweisung; Verzweigung; Abfrage; # 2 = while-Anweisung; Schleife; Abfrage; # 3 = ... main readWord Stichwort print Zwischendatei if-Anweisung Verzweigung Abfrage while-Anweisung Schleife Abfrage ... enter 1 1 1 2 2 2 Sortierung Abfrage Abfrage if-Anweisung Schleife Verzweigung while-Anweisung ... 1 2 1 2 1 2 Ausgabe 12 Abfrage 1, 2 if-Anweisung 1 Schleife 2 ... 13 Idee Paket = Sammlung zusammengehöriger Klassen (Bibliothek) Grundlagen der Programmierung Prof. H. Mössenböck 15. Pakete Zweck • mehr Ordnung in Programme bringen • bessere Kontrolle der Zugriffsrechte (wer darf auf was zugreifen) • Vermeidung von Namenskonflikten Beispiele für Pakete in der Java-Klassenbibliothek Paket enthaltene Klassen java.lang java.io java.awt java.util ... System, String, Integer, Character, Object, Math, ... File, InputStream, OutputStream, Reader, Writer, ... Button, CheckBox, Frame, Color, Cursor, Event, ... ArrayList, Hashtable, BitSet, Stack, Vector, Random, ... ... siehe: http://java.sun.com/javase/6/docs/api/ 2 Anlegen von Paketen Datei Circle.java Pakete als Sichtbarkeitsgrenzen Datei Rectangle.java package graphics; package graphics; class Circle { ... } class Rectangle { ... } Was in einem Paket deklariert ist, ist in anderen Paketen unsichtbar 1. Zeile der Datei package one; package two; class C {...} class D { C obj; } class D {...} gleicher Name stört nicht Paket graphics enthält die Klassen Circle und Rectangle Circle Felder Methoden Rectangle Felder Methoden Compiler meldet einen Fehler! C ist hier unsichtbar Paket graphics Zweck • In verschiedenen Paketen können gleiche Namen verwendet werden • Programmierer müssen nicht Rücksicht nehmen, welche Namen schon woanders verwendet wurden Wenn package-Zeile fehlt, gehören die Klassen zu einem namenlosem Standardpaket 3 4 Export von Namen Import von Klassennamen Namen können mit dem Zusatz public exportiert werden Exportierte Klassennamen können in anderen Paketen importiert werden (sie sind dann in anderen Paketen sichtbar) auch in anderen Paketen sichtbar Durch gezielten Import der Klasse Durch Qualifikation mit dem Paketnamen package one; package myPack; package myPack; public class C { int x; public int y; void p() {...} public void q() {...} C () {...} public C (int x, int y) {...} } import graphics.Circle; import one.C; class MyClass { graphics.Circle c1; java.awt.Circle c2; ... } nur in diesem Paket sichtbar class MyClass { Circle c; ... } Durch Import aller public-Klassen eines Pakets class D {...} package myPack; public-Felder und -Methoden werden nur dann exportiert, wenn die Klasse selbst public ist. Lokale Variablen und Parameter können nicht exportiert werden. 5 import graphics.*; class MyClass { Circle c; Rectangle r; ... } 6 Pakete und Verzeichnisse Geschachtelte Pakete Pakete können zu größeren Paketen zusammengefasst werden Pakete werden auf Verzeichnisse abgebildet, Klassen auf Dateien Klasse C Paket P samples Datei C.java Verzeichnis P Samples package P; class A {...} package P; class B {...} package P; class C {...} P package samples.io; public class Output beides möglich 7 import samples.graphics.Circle; importiert die Klasse Circle import samples.graphics.* importiert alle public-Klassen aus samples.graphics import samples.*; importiert alle public-Klassen aus samples (nicht aus samples.graphics) samples.io.Output out; Qualifikation einer Klasse aus einem geschachtelten Paket 8 Prinzip für Felder und Methoden • Verstecke die Implementierung komplexer Datenstrukturen vor Benutzern • Erlaube den Zugriff auf die Daten nur über Methoden private int a; int b; protected int c; nur in der Klasse sichtbar, in der das Element deklariert wurde nur im Paket sichtbar, in dem das Element deklariert wurde sichtbar: - in der deklarierenden Klasse - in deren Unterklassen (selbst wenn sie in anderen Paketen liegen) - im deklarierenden Paket auch in anderen Paketen sichtbar, wenn importiert Zugriffsmethoden (Klassenschnittstelle) "Black Box" public Klasse Stack Warum? int d; package one; • Verringert die Komplexität (Arbeiten mit den Daten wird einfacher) • Implementierung der Daten kann geändert werden, ohne dass Benutzer etwas merken • Schutz vor mutwilliger oder unabsichtlicher Zerstörung push Input.java Output.java Sichtbarkeitsattribute Daten Rectangle.java io io Information Hiding (Geheimnisprinzip) pop Circle.java Benutzung cd C:\Samples javac P/A.java push graphics samples C.java P/A P.A package samples.graphics; public class Rectangle graphics Übersetzung und Ausführung mit dem JDK java java package samples.io; public class Input A.java B.java Paket P package samples.graphics; public class Circle pop public class C { private int a; int b; protected int c; public int d; } public class D { ... } 9 b c d a d package two; import one.C; public class E extends C { } c ... public class F { ... } 10 Beispiel: Stack mit Information Hiding Dokumentationskommentare public class Stack { private int[] data; private int top; Dokumentationskommentare public Stack (int size) { data = new int[size]; top = -1; } public void push (int x) { if (top >= data.length) error("stack overflow"); else data[++top] = x; } Klassenschnittstelle /** ... */ Stack können vor die Deklarationen von Klassen, Methoden, Feldern gesetzt werden. Werkzeug javadoc erzeugt daraus Dokumentation in HTML Stack() push(int x) pop(): int /** A stack of integers. This is a FIFO data structure storing integers in a stack-like way. */ public class Stack { /** The elements in the stack. */ private int[] data; ... /** Push an integer on the stack. If the stack is full an error is reported and the program stops. */ public void push (int x) { ... } ... } public int pop () { if (top < 0) error("stack underflow"); else return data[top--]; } private void error (String msg) { Out.println(msg); System.exit(0); } 1. Satz bis Punkt wird in Kurzdoku übernommen Rest wird in Langdoku übernommen } 11 Erzeugte HTML-Datei (Stack.html) 12 javadoc Aufrufsyntax javadoc [options] {filename | packagename} Optionen • • • • -public -private -d path ... zeigt nur public-Deklarationen zeigt public- und private-Deklarationen gibt Zugriffspfad für mehrere Klassen und Pakete an Beispiel javadoc -public myDirectory/Stack.java erzeugt: myDirectory/Stack.html diverse andere Übersichtsdateien Vollständige Dokumentation von javadoc http://java.sun.com/j2se/javadoc/ 13 14 Motivation Fehler können nicht immer dort behandelt werden, wo sie auftreten Grundlagen der Programmierung void p() { ... q(); ... } Prof. H. Mössenböck 16. Ausnahmen void q() { ... r(); ... } void r() { ... ... ... } Lösung r() muss den Fehler an q() melden, q() muss ihn an p() melden, bis ihn jemand behandelt Hier tritt ein Fehler auf. Wie soll r() reagieren? • • • • • Fehlermeldung ausgeben? Programm abbrechen? einfach weiterlaufen? Korrekturmaßnahmen? ... Vielleicht möchte q() oder r() reagieren? 2 Fehlerbehandlung in früheren Zeiten Idee der Ausnahmebehandlung in Java Jede Methode lieferte einen Fehlercode geschützter Block int result; result = p(...); if (result == ok) { result = q(...); if (result == ok) { result = r(...); if (result == ok) { ... } else error(...); } else error(...); } else error(...); ... Statement; Statement; Statement; ... ... Statement; Statement; Statement; ... Ausnahmebehandler (exception handler) Wenn im geschützten Block ein Fehler (eine Ausnahme) auftritt: • Ausführung des geschützten Blocks wird abgebrochen • Fehlerbehandlungscode wird ausgeführt • Programm setzt nach dem geschützten Block fort Problem • • • • Fehlerbehandlungscode aufwändig: vor lauter Fehlerbehandlung sieht man eigentliches Programm nicht mehr Abfragen des Fehlercodes kann vergessen werden Abfragen des Fehlercodes wird oft aus Bequemlichkeit nicht durchgeführt Funktionen können neben Fehlercode kein anderes Ergebnis mehr liefern 3 4 Try-Anweisung in Java Arten von Ausnahmen try { p(...); q(...); r(...); geschützter Block } catch (Exception1 e) { error(...); } catch (Exception2 e) { error(...); void q(...) { ... throw new Exception2(); ... } Laufzeitfehler (Runtime Exceptions) Auslösen einer Ausnahme werden von der Java-VM ausgelöst • Division durch 0 • Zugriff über null-Zeiger • Indexüberschreitung ArithmeticException NullPointerException ArrayIndexOutOfBoundsException Müssen nicht behandelt werden Wenn sie nicht behandelt werden, stürzt das Programm mit einer Fehlermeldung ab Ausnahmebehandler } catch (Exception3 e) { Geprüfte Ausnahmen (Checked Exceptions) error(...); werden vom Benutzercode ausgelöst (throw-Anweisung) } • vordefinierte Ausnahmen • selbst definierte Ausnahmen Vorteile z.B. FileNotFoundException z.B. MyException Müssen behandelt werden. Compiler prüft, ob sie abgefangen werden • Fehlerfreier Fall und Fehlerfälle sind sauberer getrennt • Man kann nicht vergessen, einen Fehler zu behandeln (Compiler prüft, ob es zu jeder möglichen Ausnahme einen Behandler gibt) 5 Hierarchie der Ausnahmeklassen Throwable Error LinkageError VirtualMachineError ... Exception RuntimeException NullPointerException ArithmeticException IndexOutOfBoundsException ... IOException FileNotFoundException ... AWTException ... MyException1 MyException2 ... 6 Ausnahmen sind als Klassen codiert Fehlerinformationen stehen in einem Ausnahme-Objekt Fehler, die nicht am Programm liegen (müssen nicht abgefangen werden) von der VM ausgelöste Fehler (müssen nicht abgefangen werden) class Exception extends Throwable { Exception(String msg) {...} // erzeugt neues Ausnahmeobjekt mit Fehlermeldung String getMessage() {...} // liefert gespeicherte Fehlermeldung String toString() {...} // liefert Art der Ausnahme und gespeichert Fehlermeldung void printStackTrace() {...} // gibt Methodenaufrufkette aus ... } Eigene Ausnameklasse (speichert Informationen über speziellen Fehler) class MyException extends Exception { private int errorCode; MyException(String msg, int errorCode) {...} int getErrorCode() {...} // toString(), printStackTrace(), ... von Exception geerbt } vordefinierte Ausnahmen (müssen abgefangen werden) selbst definierte Ausnahmen (müssen abgefangen werden) 7 8 Throw-Anweisung catch-Blöcke und finally-Block Löst eine Ausnahme aus try { ... } catch (MyException e) { Out.println(e.getMessage() + ", error code = ", + e.getErrorCode()); } catch (NullPointerException e) { ... } catch (Exception e) { ... } finally { ... } throw new MyException("invalid operation", 17); "Wirft" ein Ausnahmeobjekt mit entsprechenden Fehlerinformationen • • • • bricht normale Programmausführung ab sucht passenden Ausnahmebehandler (catch-Block) führt Ausnahmebehandler aus und übergibt ihm Ausnahmeobjekt als Parameter setzt nach try-Anweisung fort, zu der der catch-Block gehört • Passender catch-Block wird an Hand des Ausnahme-Typs ausgewählt • catch-Blöcke werden sequentiell abgesucht Achtung: speziellere Ausnahme-Typen müssen vor allgemeineren stehen • Am Ende wird (optionaler) finally-Block ausgeführt egal, ob im geschützten Block ein Fehler auftrat oder nicht 9 Zweck des finally-Blocks Ablauflogik bei Ausnahmen main() Zum Sicherstellen, dass Abschlussarbeiten auch im Fehlerfall ausgeführt werden try { FileStream s = new FileStream(...); ... ... ... s.close(); } catch (...) { ... } falsch Datei wird im Fehlerfall nicht geschlossen 10 try { f(); ... } catch (E1 e) { ... } ... FileStream s; try { s = new FileStream(...); ... ... ... } catch (...) { ... } finally { s.close(); } f() try { g(); ... } catch (E2 e) { ... } finally { ... } ... g() if (...) { throw new E1(); } else if (...) { throw new E2(); } else { throw new E3(); } ... throw new E1(); • • • • richtig Datei wird auf jeden Fall geschlossen 11 keine try-Anweisung in g() bricht g() ab kein passender catch-Block in f() führt finally-Block in f() aus und bricht f() dann ab führt catch-Block für E1 in main() aus setzt nach try-Anweisung in main() fort 12 Ablauflogik bei Ausnahmen main() try { f(); ... } catch (E1 e) { ... } ... f() Ablauflogik bei Ausnahmen g() try { g(); ... } catch (E2 e) { ... } finally { ... } ... main() if (...) { throw new E1(); } else if (...) { throw new E2(); } else { throw new E3(); } ... try { f(); ... } catch (E1 e) { ... } ... f() try { g(); ... } catch (E2 e) { ... } finally { ... } ... g() if (...) { throw new E1(); } else if (...) { throw new E2(); } else if (...) { throw new E3(); } ... throw new E2(); throw new E3(); • • • • • Compiler meldet einen Fehler, weil E3 nirgendwo in der Ruferkette abgefangen wird keine try-Anweisung in g() bricht g() ab führt catch-Block für E2 in f() aus führt finally-Block in f() aus setzt nach try-Anweisung in f() fort fehlerfreier Fall 13 • • • • führt g() zu Ende aus führt try-Block in f() zu Ende aus führt finally-Block in f() aus setzt nach finally-Block in f() fort 14 Spezifikation von Ausnahmen im Methodenkopf Eigentlich müsste es heißen Wenn eine Methode eine Ausnahme an den Rufer weiterleitet, muss sie das in ihrem Methodenkopf mit einer throws-Klausel spezifizieren void main() throws E3 void f() throws E1, E3 void g() throws E1, E2, E3 try { f(); ... } catch (E1 e) { ... } ... try { g(); ... } catch (E2 e) { ... } finally { ... } ... if (...) { throw new E1(); } else if (...) { throw new E2(); } else if (...) { throw new E3(); } ... void f() { try { ... g(); ... } catch (E2 e) { ... } } void g() throws E2 { try { ... throw new E1(); ... throw new E2(); ... } catch (E1 e) { ... } } Compiler weiß dadurch, dass g() eine E2-Ausnahme auslösen kann. Wer g() aufruft, muss daher • entweder E2 abfangen • oder E2 im eigenen Methodenkopf mit einer throws-Klausel spezifizieren Man kann nicht vergessen, eine Ausnahme zu behandeln! 15 16 Weitere Java-Themen Klassen Prof. H. Mössenböck - 17. Ausblick Threads Grundlagen der Programmierung innere Klassen anonyme Klassen abstrakte Klassen Interfaces generische Klassen und Interfaces Bibliotheken - GUI (Swing, AWT) Collections (Listen, Hashtabellen, ...) Streams (Ein-/Ausgabe) Net und Web Reflection Anwendungen - Applets - Servlets und Java Server Pages - Java Beans 2