12. „Advanced Features“, Abschluss • Abstrakte Klassen • Ausnahmen • Rückblick und Ausblick László Böszörményi ESOP Abschluss - 1 12. 1. Abstrakte Klassen • Am oberen Ende einer Hierarchie finden wir oft eine reine Abstraktion – • • Möbel, Fahrzeug, Vogel, Tier etc. Java bietet dafür abstrakte Klassen an – Diese enthalten zumindest eine abstrakte Methode – Abstrakte Methoden haben nur Methodenkopf (keinen Rumpf) – Sie werden erst später (in einer Subklasse) implementiert (durch Überschreiben) – Abstrakte Klassen können nicht angelegt werden Interfaces sind spezielle abstrakte Klassen – Alle Methoden sind abstrakt – Es gibt keine Variablendeklarationen László Böszörményi ESOP Abschluss - 2 Fahrzeug- Modell Abstrakte Superklasse Jedes Fahrzeug hat: Bewegung, Größe, Preis ... Fahrzeug PKW LKW Cabrio Schlepper Fahrzeuge an sich gibt es nicht: Die Extension der abstrakten Klasse ist leer Subklasse von Fahrzeug und Superklasse von Schlepper Fahrzeug Cabrio LKW PKW Schlepper Ein PKW hat Kofferraum, Kindersitz ... László Böszörményi ESOP Abschluss - 3 Abstrakte Basisklasse mit Ordnung • Wir definieren eine generische Liste, die beliebige Objekte speichern kann, die von einer Basisklasse abgeleitet werden • Eine abstrakte Klasse (nennen wir sie BasicObject) • Alternativlösung wäre: Elemente sind Objects, die ein gemeinsames Interface implementieren • Elemente an der Liste sind Subklassen von BasicObject • Vorteil • – Kann default-Implementierungen anbieten – Alle Eigenschaften werden vererbt Nachteil: Mehrfachvererbung nicht möglich László Böszörményi ESOP Abschluss - 4 Klasse BasicObject public abstract class BasicObject { // BasicObject mit unique ID und Vergleich abstract int compareWith (BasicObject x); // Rückgabewert: /* < 0, wenn this ist kleiner als x > 0, wenn this ist größer als x = 0, wenn this und x gleich sind */ private static long ident = 0; // Statischer Zähler: für den eindeutigen ID private long id; // Identifier des aktuellen Objektes BasicObject() { ident ++; id = ident; }; // Jedes Objekt erhält eigenen ID public String toString () {return ("[" + id + "]"); } // BasicObject-s werden mit vorangestellten: [id] ausgeschrieben } // BasicObject László Böszörményi ESOP Abschluss - 5 Node und List mit BasicObject Info-Teil public class Node { // 03.11.2000. LB BasicObject info; // info ist nun ein Objekt, das Node // die Klasse BasicObject erweitert next; public Node (BasicObject info, Node next) // Konstruktor { this.info = info; this.next = next;} } // Node public interface List { // 03.11.2000. LB public boolean search (BasicObject key); // Die Schlüsselwerte sind public void insert(BasicObject key); // Subklassen von BasicObject public boolean remove (BasicObject key); public String toString (); } // List László Böszörményi ESOP Abschluss - 6 CompInt als Subklasse von BasicObject public class CompInt extends BasicObject { // 20.11.2000. LB private int i; // Wert des CompInt-Objektes public CompInt(int num) {i = num; } // Konstruktor public String toString () { // Überschreibt und benutzt return super.toString() + i; // die Methode der Superklasse: } // toString // toString von BasicObj public int compareWith (BasicObject x) { if (i < ( (CompInt) x).i) return -1; else if (i > ( (CompInt) x).i) return 1; else // Implementiert die abstrakte // Methode der Superklasse return 0; } // compareWith } // CompInt László Böszörményi ESOP Abschluss - 7 Person als Subklasse von BasicObject public class Person extends BasicObject { // 20.11.2000. LB private String name; // Name der Person public Person (String person_name) { // Konstruktor name = person_name; } // Person public String toString () { return super.toString() + name; // Überschreibt und benutzt toString // der Superklasse BasicObject } // toString public int compareWith (BasicObject p2) { // Implementiert die return this.name.compareTo( ( (Person) p2).name ); // abstrakte Methode } // compareWith // Benutzt die compareTo-Methode der String-Klasse } // Person László Böszörményi ESOP Abschluss - 8 Benutzung bleibt gleich static void useList(List list) { String [] persons1 = {"Pete", "May", "Paul", "Bob"}; String [] persons2 = {"Sue", "Mic", "Xe", "Xa", "Ada", "Xu"}; for (int i = persons1.length - 1; i >= 0; i -- ) list.insert ( new Person (persons1[i]) ); Out.println(list); for (int i = 0; i < persons2.length; i++ ) list.insert( new Person (persons2[i]) ); Out.println(list); for (int i =0; i < persons2.length; i+= 3 ) { Person p = new Person (persons2[i]); String s; if ( list.remove (p) ) s = " entfernt"; else s = " nicht enthalten"; Out.println(p + s); } // for i ( [1]Bob [3]May [2]Paul [4]Pete ) Out.println(list); ( [9]Ada [1]Bob [3]May [6]Mic [2]Paul [4]Pete [5]Sue [8]Xa [7]Xe [10]Xu ) [11]Sue entfernt } // useList [12]Xa entfernt ( [9]Ada [1]Bob [3]May [6]Mic [2]Paul [4]Pete [7]Xe [10]Xu ) László Böszörményi ESOP Abschluss - 9 Implementierung der generischen Liste (1) public class NonRekList implements List { // 03.11.2000. LB private Node head = null; public boolean search (BasicObject key) { Node actual = head; while ( actual != null && key.compareWith(actual.info) != 0 ) actual = actual.next; return actual != null; } // search public String toString () { Node actual = head; String listString = ""; while (actual != null ) { listString += actual.info + " "; actual = actual.next; } // while return "( " + listString + ")"; } // toString László Böszörményi ESOP Abschluss - 10 Implementierung der generischen Liste (2) public void insert (BasicObject key) { if ( (head == null) || ( key.compareWith(head.info) < 0 ) ) head = new Node(key, head); else { Node previous = head, actual = head.next; while (actual != null && key.compareWith(actual.info) > 0) { previous = actual; actual = actual.next; } // while previous.next = new Node(key, actual); } // if } // insert László Böszörményi ESOP Abschluss - 11 Implementierung der generischen Liste (3) public boolean remove (BasicObject key) { if (head == null) return false; else if ( key.compareWith(head.info) == 0 ) { head = head.next; return true; } // if (head.info == key) else { Node previous = head, actual = head.next; while (actual != null && key.compareWith (actual.info) != 0 ) { previous = actual; actual = actual.next; } // while if (actual != null) previous.next = actual.next; return actual != null; } // if (head == null) } // remove } // NonRekList László Böszörményi ESOP Abschluss - 12 12. 2. Ausnahmen • Reaktionen auf fehlerhafte Ereignisse sollten als Ausnahmen (exceptions) behandelt werden – Damit wir der „normale“ Teil einfacher – Die Versuchung ist kleiner, Fehler unbehandelt zu lassen • Behandlung der Fehler kann nicht immer dort erfolgen, wo der Fehler aufgetreten ist: sie muss dann delegiert werden. • Beispiele – Falsche Eingabe von der Tastatur: Benutzer wiederholen lassen – Falsches Dateiformat • Benutzer anfordern, einen anderen Dateinamen anzugeben • Aufrufer kann vielleicht ohne diese Daten weiterrechnen: delegieren – Falscher Index bei einem Array, null-Wert bei einer Objektvariable: • Vielleicht kann der Aufrufer die Situation noch „retten“ – oder Abbruch László Böszörményi ESOP Abschluss - 13 Exceptions in Java • Eine Ausnahme (exception) ist ein spezielles Objekt • Eine Ausnahme kann – Erzeugt („geworfen“ – throw), – Behandelt („aufgefangen“ – catch) und – „Angekündigt“ werden (throws im Kopf einer Methode) • Eine Ausnahme unterbricht die Ausführung der Anweisungen • Ein Handler wird gesucht, die die Ausnahme bearbeitet • Nach der Bearbeitung läuft das Programm weiter László Böszörményi ESOP Abschluss - 14 Die try-Anweisung try { Folge der „normalen“ Anweisungen – die den fehlerfreien Fall bearbeiten } // try catch (Exception1 e1) { // Die catch-Zweige werden der Reihe nach durchgesucht Behandlung der Ausnahmen der Klasse Exception1 (und seiner Subklassen) } // catch Exception1 catch (Exception2 e2) { Behandlung der Ausnahmen der Klasse Exception2 (und seiner Subklassen) } // catch Exception2 finally { Diese Anweisungen werden auf jeden Fall ausgeführt, unabhängig davon, ob der try-Zweig normal abgelaufen ist, oder unterbrochen wurde (durch Ausnahme, oder break oder return). } // finally László Böszörményi ESOP Abschluss - 15 Ausführung der try-Anweisung 1. Die Anweisungsfolge des try-Blocks wird ausgeführt 2. Tritt eine Ausnahme E auf, dann wird ein Handler gesucht, und sein Block ausgeführt 1. Der erste catch-Block (von oben nach unten) mit einer Ausnahmeklasse A, die eine Superklasse von E ist (E ⊂ A) • Das Ausnahmeobjekt wird der Variable im catch-Klausel zugewiesen 2. Falls kein entsprechender catch-Block gefunden 1. Suche in den äußeren Blöcken, von innen nach außen 2. Suche entlang der Aufrufskette aufwärts • Wenn bis in die main Methode kein Handler gefunden: Abbruch, Fehler 3. Wenn ein finally-Block definiert ist, wird er auf jeden Fall ausgeführt 4. Nach der Fehlerbehandlung läuft das Programm nach der try-Anweisung weiter László Böszörményi ESOP Abschluss - 16 Hierarchie von Exceptions-Klassen • Oberste Exception-Klasse: java.lang.Throwable – Methode zur Fehlermeldung (geerbt durch alle): String Throwable.getMessage() – Weitere Felder und Methoden können definiert werden • Vordefinierte Unterklassen: – java.lang.Error • Für Fehler, die meistens nicht reparierbar sind: Fehlermeldung – java.lang.Exception • Für Fehler, die sinnvoll behandelt werden können, z.B: • java.io.InterruptedIOException – mit der zusätzlichen Methode: – László Böszörményi public int bytesTransferred(): Anzahl der übertragenen Bytes ESOP Abschluss - 17 Muster der korrekten Dateibehandlung try { // Der „normale“ Teil des Programms: readFromFile("File_Name“); ... // Datei wird gesucht, eröffnet und bearbeitet } // try catch (FileNotFoundException e) { Behandlung des Falls, die Datei wurde nicht gefunden } // catch FileNotFoundException catch (IOException e) Fehlerbehandlung { Behandlung des Falls, die Datei enthält fehlerhafte Daten } // catch IOException catch (MyException e) { Behandlung von benutzerdefinierten Fehlern } // catch MyException finally file.close(); { // Datei wird auf jeden Fall geschlossen } // finally László Böszörményi ESOP Abschluss - 18 Behandlung falscher Eingabe static int GetInt() throws NumberFormatException { // Leitet die Ausnahme nur weiter String s = ""; try {s = new BufferedReader (new InputStreamReader (System.in)).readLine(); } catch (IOException e) {Out.println("Grober Fehler in der Eingabe");} return Integer.parseInt (s); // Erzeugt die Ausnahme NumberFormatException } // GetInt static void main(String [] args) { int result, dividend, divisor; xxx Fehler: keine Zahl 12 qqq Fehler: keine Zahl try { dividend = GetInt(); divisor = GetInt(); result = 0; while (dividend >= divisor) { result++; dividend -= divisor; } Ergebnis, falls Eingabe korrekt Out.println("Ganzzahliger Quotient = " + result); } catch (NumberFormatException e) {Out.println("Fehler: keine Zahl");} } // main László Böszörményi ESOP Abschluss - 19 Benutzerdefinierte Ausnahme class MyException extends Exception { // Eigene Ausnahme-Klasse String text; // Beschreibung der Ausnahme MyException (String t) { text = t;} // Konstruktor public String getMessage() { return text; } // Überschreibt getMessage } // MyException ... static int GetPosInt () throws NumberFormatException, MyException { // Ankündigung String s = ""; try {s = new BufferedReader (new InputStreamReader (System.in)).readLine(); } catch (IOException e) {Out.println("Grober Fehler in der Eingabe");} int i = Integer.parseInt (s); // Ausnahme die hier auftritt, wird weitergeleitet if (i <= 0) throw new MyException ("Eingabe nicht positiv"); // Erzeugt Ausnahme return i; } // GetPosInt László Böszörményi ESOP Abschluss - 20 Zyklische Behandlung von Ausnahmen static void main(String [] args) { while (true) { // Die Schleife wird durch break explizit untebrochen try { Out.println("Dividend und Divisor:"); int dividend = GetPosInt(), divisor = GetPosInt(), result = 0; while (dividend >= divisor) { result++; dividend -= divisor; } Out.println("Ganzzahliger Quotient = " + result); break; // Normale Ausführung endet Dividend und Divisor: 12 -3 MyException: Eingabe nicht positiv Dividend und Divisor: qqq java.lang.NumberFormatException: keine Zahl Dividend und Divisor: 12 3 Ganzzahliger Quotient = 4 } // try catch (NumberFormatException e) {Out.println(e + " keine Zahl");} catch (MyException e) {Out.println(e);} } // while } // main László Böszörményi Gibt den Namen der Exception auch aus ESOP Abschluss - 21 Schachtelung von try und catch Blöcken try { try { Fehler: java.lang.ArithmeticException: / by zero Fehler2: java.lang.ArithmeticException: / by zero int i = 5 / 0; } // try catch (ArithmeticException e) { Out.println("Fehler: " + e ); // Default-Text vom ArithmeticException int j = 5 / 0; // Ausnahme in Ausnahme: Wird im einschließenden Block } // catch e // behandelt: führt zu keiner unendlichen Ausnahmebehandlung } // try catch (ArithmeticException e2) { Out.println("Fehler2: " + e2 ); // Default-Text vom ArithmeticException } // catch e2 László Böszörményi ESOP Abschluss - 22 Compilationsunsicherheit durch try • Die Anweisungsfolge in try kann an beliebiger Stelle unterbrochen werden • Der Compiler kann nicht wissen, wann das geschieht void myMethod() { int x, y, z = GetInt(); try { // Könnte 0 eingelesen werden y = 1; x = 5; y = x / z; // Verursacht ArithmeticException, falls z == 0 } // try catch (ArithmeticException e) { Out.println("Fehler: " + e );} int i = x; // Fehler bei der Compilation: variable x might not have been initialized } // myMethod László Böszörményi ESOP Abschluss - 23 12. 4. Rückblick und Ausblick • Grundkonzepte der strukturierten und objektorientierten Programmierung und ihre Abbildung auf Java erarbeitet • Die Entwicklung von Softwaresystemen benötigt noch viel mehr (siehe Lehrveranstaltungen später) • Wir finden meistens eine große Anzahl von vorgefertigten Klassen, die ein Framework bilden • Die Benutzung von Frameworks macht die Programmierung viel effizienter – der Aufwand fürs Erlernen der Frameworks kann aber beachtlich sein László Böszörményi ESOP Abschluss - 24