Schon mal einen Blue Screen gesehen? Ausnahmebehandlung OOPM, Ralf Lämmel Beispiele für Ausnahmesituationen Programmfehler “precondition failed” Unzulässige Nutzereingabe “bad url” Nichtverfügbarkeit von Ressourcen “connection lost” Verausgabung von Ressourcen “stack overflow” (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 927 Verhalten eines Programmes bei unzulässigen Eingaben $ java Program 3 4 3+4=7 $ java Program 3 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1 at Program.main(Program.java:12) $ java Program 3 vier Exception in thread "main" java.lang.NumberFormatException: For input string: "vier" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:447) at java.lang.Integer.parseInt(Integer.java:497) at Program.main(Program.java:12) Können wir uns bessere Fehlermeldungen vorstellen? (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 928 Verhalten des Programmes mit Ausnahmebehandlung $ java Program 3 4 3+4=7 $ java Program 3 Incorrect number of arguments $ java Program 3 vier Argument(s) not a number (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 929 Quellen für Ausnahmesituationen im Programm Haben Sie noch eine weitere Idee für eine Ausnahmesituation? public class Program { public static void main(String[] args) { Feldzugriff kann scheitern int value1 = Integer.parseInt(args[0]); int value2 = Integer.parseInt(args[1]); int sum = value1 + value2; Konvertierung in Zahl kann scheitern System.out.println(value1 + " + " + value2 + " = " + sum); } } Illustrationen für Ausnahmebehandlung siehe Repository: idioms.errorhandling (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 930 Ansatz der Ausnahmebehandlung public class Program { public static void main(String[] args) { Teste dass args.length == 2 int value1 = Integer.parseInt(args[0]); int value2 = Integer.parseInt(args[1]); int sum = value1 + value2; Behandle nichterfolgreiche Konvertierung System.out.println(value1 + " + " + value2 + " = " + sum); } } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 931 Allgemeine Fragestellungen 1. Was ist ein Fehler bzw. eine Ausnahme? 2. Wie wissen wir, dass ein Fehler bzw. eine Ausnahme auftritt? 3. Wie weiß ein Programm dies? 4. Wie reagiert ein Programm auf einen Fehler bzw. eine Ausnahme? 5. Kann das Programm sich erholen? (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 932 Fehler Der Begriff ist hochgradig überladen. Definition: ein unerlaubter Programmzustand Beispiel: das Beispielprogramm darf nicht in dem Zustand sein, dass es in die main-Funktion eintritt wobei nur 1 Eingabewert ansteht. Naives Modell der Fehlervermeidung: Die Spezifikation von Methoden, Klassen und Programmen definiert was erlaubte Programmzustände sind. Jede Verwendung der Methoden, Klassen und Programmen wird im Voraus auf die Einhaltung der Spezifikation überprüft. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 933 Ziele der Fehlerbehandlung Vermeidung von inkonsistentem Verhalten Rückkehr zu früherem, sicherem Programmpunkt Kontrollierte Beendung des Programmes Sicherung von Ergebnissen Ausgabe von hilfreichen Fehlermeldungen (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 934 Techniken zur Fehlerbehandlung Keine Behandlung Terminierung des Programmes Verwendung eines globalen Fehlerflags Verwendung eines speziellen Rückgabewertes Vereinbarung einer speziellen Funktion Verwendung von Transaktionsmechanismen Ausnahmebehandlung mit try/catch (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 935 Techniken zur Fehlerbehandlung Keine Behandlung Terminierung des Programmes Verwendung eines globalen Fehlerflags Verwendung eines speziellen Rückgabewertes Vereinbarung einer speziellen Funktion Verwendung von Transaktionsmechanismen Ausnahmebehandlung mit try/catch (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 936 Verwendung eines globalen Fehlerflag Wir “erfinden” java.lang.Integer.error. Wir ignorieren das Problem der Feldgröße im folgenden. int value1 = Integer.parseInt(args[0]); boolean error = Integer.error; Abfrage des Fehlerflags nach jedem Aufruf int value2 = Integer.parseInt(args[1]); error |= Integer.error; if (!error) { int sum = value1 + value2; System.out.println(value1 + " + " + value2 + " = " + sum); } else { System.err.println("Argument(s) not a number"); System.exit(-1); } Das Abfragen des Flags kann leicht vergessen werden. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 937 Techniken zur Fehlerbehandlung Keine Behandlung Terminierung des Programmes Verwendung eines globalen Fehlerflags Verwendung eines speziellen Rückgabewertes Vereinbarung einer speziellen Funktion Verwendung von Transaktionsmechanismen Ausnahmebehandlung mit try/catch (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 938 Verwendung eines speziellen Rückgabewertes Wir ändern den Ergebnistyp von parseInt von int nach Integer. (Integer ist ein Referenztyp für int.) Wir interpretieren null als Fehler. Integer value1 = Integer.parseInt(args[0]); boolean error = value1 == null; Integer value2 = Integer.parseInt(args[1]); error |= value2 == null; Interpretiere null als Fehler if (!error) { int sum = value1 + value2; System.out.println(value1 + " + " + value2 + " = " + sum); } else { System.err.println("Argument(s) not a number"); System.exit(-1); } Fehler können weiterhin übersehen werden oder der Fehlerwert kann fehlinterpretiert werden. Auch verlangt diese Technik häufig Typänderungen. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 939 Techniken zur Fehlerbehandlung Keine Behandlung Terminierung des Programmes Verwendung eines globalen Fehlerflags Verwendung eines speziellen Rückgabewertes Vereinbarung einer speziellen Funktion Verwendung von Transaktionsmechanismen Ausnahmebehandlung mit try/catch (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 940 Vereinbarung einer speziellen Funktion Illustration für Cobol; Vereinbarung eines Fehlerparagraphen für bestimmte Dateifehler. DECLARATIVES. HANDLE-FILE-ERRORS SECTION. USE AFTER ERROR ON ACCOUNT-FILE. HANDLE-ACOUNT-ERROR. MOVE “ACOUNT” TO PANIC-RESOURCE. MOVE “FILE ERROR” TO PANIC-HEADER. MOVE FILE-STATUS TO PANIC-CODE. GO TO PANIC-STOP. END-DECLARATIVES. Diese Technik verlangt Sprachunterstützung. Ausnahmen (siehe nachfolgend) sind allgemeiner. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 941 Techniken zur Fehlerbehandlung Keine Behandlung Terminierung des Programmes Verwendung eines globalen Fehlerflags Verwendung eines speziellen Rückgabewertes Vereinbarung einer speziellen Funktion Verwendung von Transaktionsmechanismen Ausnahmebehandlung mit try/catch (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 942 Ausnahmebehandlung mit try/catch try { int value1 = Integer.parseInt(args[0]); int value2 = Integer.parseInt(args[1]); int sum = value1 + value2; System.out.println(value1 + " + " + value2 + " = " + sum); } catch (NumberFormatException e) { System.err.println("Argument(s) not a number"); System.exit(-1); } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 943 Der OO-Ansatz: Ausnahmen Ausnahme: ein Ereignis während der Ausführung des Programms welches zur Unterbrechung des regulären Ablaufs führt. Java-Repräsentation einer Ausnahme: Objekte der Klasse Throwable oder deren Unterklassen. Werfen einer Ausnahme: Bei der Feststellung eines Fehlers wird (vom Laufzeitsystem oder vom Programm) ein Ausnahmeobjekt konstruiert und an das Laufzeitsystem zur Behandlung übergeben. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 944 Vom Programm geworfene Ausnahme Parameter für Diagnose. public class Program { public static void main(String[] args) { throw new RuntimeException("Don’t call main!"); } } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau Ein konkreter Ausnahmetyp 945 Vordefinierte Ausnahmeklassen Ausnahmen werden durch Klassen in einer Vererbungshierarchie organisiert. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 946 Wichtige Unterklassen von Throwable Error Klasse und Unterklassen: Kritischer Fehler von dem kaum zu erwarten ist, dass die Applikation ihn behandeln kann. Virtual Machine Error Exception Klasse und Unterklassen: Mögliche Ausnahme in der “normalen” Programmausführung. Solche Ausnahmen können behandelt werden. IOException NullPointerException (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 947 Ausnahmen des Laufzeitsystems IndexOutOfBoundsException: Der Index bei einem Feldzugriff ist nicht im zulässigen Bereich. ArrayStoreException: Der Typ des Objektes bei einem schreibenden Feldzugriff ist nicht zulässig. NullPointerException: Ein Feldzugriff oder ein Methodenaufruf auf “null” anstatt einem Objekt. SecurityException: Der Security Manager weist einen Verstoss gegen die Sicherheitspolitik ab. .... (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 948 Sprachunterstützung für Ausnahmebehandlung try { ... // Anweisungen die Ausnahmen werfen könnten } catch(AusnahmeTyp name) { ... // Anweisungen welche die Ausnahme behandeln } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 949 Ausnahmebehandlung mit try/catch try { int value1 = Integer.parseInt(args[0]); int value2 = Integer.parseInt(args[1]); int sum = value1 + value2; System.out.println(value1 + " + " + value2 + " = " + sum); } catch (NumberFormatException e) { System.err.println("Argument(s) not a number"); System.exit(-1); } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 950 Sprachunterstützung für Ausnahmebehandlung einschließlich des finally-Blockes try { try-Block ... // Anweisungen Ausnahme } catch (AusnahmeTyp name) { ... // Anweisungen Keine Ausnahme } finally { ... // Anweisungen catchBlock finallyBlock } Der catch-Block ist anwendbar für eine Ausnahme a wenn der Typ von a zuweisungskompatibel mit AusnahmeTyp ist. Der finally-Block wird auch für den Fall ausgeführt, dass der catch-Block nicht anwendbar ist oder der catch oder try-Block continue oder break benutzt. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 951 Mehrere Catch-Klauseln try { … } catch (AusnahmeTyp1 name1) { … } catch (AusnahmeTyp2 name2) { … } finally { … } Klauseln werden in Definitionsreihefolge geprüft. Speziellere Ausnahmetypen sind zuerst anzugeben! (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 952 Ein Tabu-Idiom try { ... } catch (Exception x) { } Konsumiert alle Ausnahmen. Behandelt keine Ausnahmen. Verursacht eventuell schwierige Folgefehler. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 953 Propagierung von Ausnahmen Innerhalb von Hierarchien von Throw-Blöcken zum unmittelbar umfassenden throw-Block Im Aufrufkeller zum nächsten Aufruf mit einem Handler (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 954 Illustration für Propagierung public class Program { public static void a() { b(); } public static void b() { c(); } public static void c() { throw new RuntimeException("Safe Our Souls!"); } public static void main(String[] args) { try { a(); } catch (RuntimeException e) { e.printStackTrace(); } } } java.lang.RuntimeException: Safe Our Souls! at Program.c(Program.java:9) at Program.b(Program.java:6) at Program.a(Program.java:3) at Program.main(Program.java:13) (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 955 “Stack trace” Parameter der Ausnahme Ausnahmetyp java.lang.RuntimeException: Safe Our Souls! at Program.c(Program.java:9) at Program.b(Program.java:6) at Program.a(Program.java:3) at Program.main(Program.java:13) Methode der Behandlung (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 956 Aufrufkeller und Ausnahmebehandlung (für ein neues Szenario) Methode wirft Ausnahme Methode mit Ausnahme Methode reicht Ausnahme durch Methodenaufruf Methode ohne Exception handler Methode behandelt Ausnahme Methodenaufruf Methode mit Exception handler Methodenaufruf Methode ist “unbehelligt” main (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 957 Wichtige Regeln Konsumiere nur die Ausnahmen, die Du auch behandelst (behandeln kannst). Wenn sich eine Ausnahme als nicht behandelbar herausstellt, nachdem sie bereits abgefangen wurde, dann muss sie erneut geworfen werden. Verwende Klassenvererbung um ein System von Ausnahmen in einer Hierarchie zu organisieren. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 958 Wann sollte man eine Ausnahme werfen anstatt einfach zurückzukehren? Pro Ausnahmewurf Es handelt sich um eine wirkliche Ausnahmesituation. Der Aufrufer sollte das Problem nicht ignorieren können. Die Klasse wäre sonst instabil oder inkonsistent. Con Ausnahmewurf Das Problem kann allgemein lokal behoben werden. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 959 Ein weiteres Beispiel: Abhebung vom Konto Ausnahmesituationen Grundlegendes Problem (als Folge eines Bedienfehlers oder einer Vertragsverletzung) Betrag ist negativ IllegalArgumentException Betrag überzieht Konto OverdraftException Anwendungsspezifisches Problem, welches behandelt werden sollte. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 960 Abhebung vom Konto mit Überziehungskontrolle /** * Withdraw some money from account. * Refuse negative amounts as nonsense. * Throw an exception if an attempt is made to overdraw. */ public void withdraw(float amount) Vordefinierte { Ausnahmeklasse // Precondition if (amount < 0) throw new IllegalArgumentException(); if (balance < amount) throw new OverdraftException(); balance -= amount; } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau Programm-definierte Ausnahmeklasse 961 Programm-definierte Ausnahmenklassen public class OverdraftException extends RuntimeException { } Unterklassen von Exception Konstruktoren Standardkonstruktor Konstruktor mit String-Parameter (“Fehlermeldung”) Weiterer Zustand Daten für Handler (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau Für fortgeschrittene Szenarien 962 Verwendung von Zusicherungen vs. Ausnahmen public void withdraw(float amount) { // Precondition assert !( amount < 0 || balance < amount); balance -= amount; } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 963 Verletzten der Vorbedingung führt zum Werfen einer Ausnahme AssertionError. Verwendung von Zusicherungen vs. Ausnahmen public void withdraw(float amount) { Spezifischere Ausnahme als AssertionError // Precondition if (amount < 0) throw new IllegalArgumentException(); if (balance < amount) throw new OverdraftException(); balance -= amount; } Wir werden zeigen, dass wir den Aufrufer zur Behandlung zwingen wollen und können! (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 964 Zwang zur Ausnahmebehandlung Gedankenexperiment Was wäre wenn wir Aufrufer von “withdraw” zur Ausnahmebehandlung “ermutigen” (“erzwingen”) wollen -- soweit es den Versuch der Kontenüberziehung angeht? Ein solcher Versuch ist schließlich eine Ausnahmesituation, auf welche das Programm vorbereitet sein sollte. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 965 Wir wollen dieses Programm verbieten. public static void transfer( Account from , Account to , float amount) { to.deposit(amount); from.withdraw(amount); } Der Compiler (das Typsystem) soll den Programmierer zwingen, den möglichen Fehler zu behandeln. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 966 Verwendung geprüfter Ausnahmen public class OverdraftException extends RuntimeException { } Änderung unserer ursprünglichen Ausnahmeklasse Ungeprüfte Ausnahmen Sind von RuntimeException abgeleitet Müssen nicht in Methodensignatur spezifiziert werden Gelten eher als nicht reparierbar Geprüfte Ausnahmen Sind nicht von RuntimeException abgeleitet Müssen in Methodensignatur spezifiziert werden Sollten von der Anwendung behandelt werden (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 967 Dieses Programm wird abgewiesen. public static void transfer( Account from , Account to , float amount) { to.deposit(amount); from.withdraw(amount); } Wir müssen die Ausnahme OverdraftException entweder deklarieren oder behandeln. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 968 Eine Behandlung innerhalb der Methode transfer ist nicht offensichtlich. public static void transfer( Account from , Account to , float amount) { try { from.withdraw(amount); to.deposit(amount); } catch (OverdraftException o) { // ... but what to do? } } (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 969 Die Behandlungspflicht ergeht an Aufrufer. public static void transfer( Account from , Account to , float amount) throws OverdraftException { from.withdraw(amount); to.deposit(amount); } Der Compiler (das Typsystem) soll den Programmierer wenigstens dazu zwingen, die Ausnahme zu deklarieren. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 970 Die mögliche Ausnahme wird explizit deklariert. Deklaration von werfbaren Ausnahmen /** * Withdraw some money from account. * Refuse negative amounts as nonsense. * Throw an exception if an attempt is made to overdraw. */ public void withdraw(float amount) throws OverdraftException { // Precondition if (amount < 0) IllegalArgumentExceptio n ist eine ungeprüfte Ausnahmeklasse und muss deswegen nicht deklariert werden. throw new IllegalArgumentException(); if (balance < amount) throw new OverdraftException(); balance -= amount; } Der Compiler (das Typsystem) verlangt die throws-Deklaration (im Falle einer geprüften (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 971 Geprüft oder ungeprüft? Sun´s (Oracle’s?) guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 972 Zusammenfassung Fehler sind unzulässige Programmzustände. Ausnahmen sind Ereignisse, die den regulären Ablauf abbrechen. Ausnahmen sind Objekte / Ausnahmetypen sind Klassen. Sprachunterstützung: try, catch, finally, throw, throws Von einigen Fehler kann man sich nicht erholen. Von gewissen Ausnahmen kann man sich erholen. Ausblick Syntax von Softwaresprachen Klausur (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau Ein Zitat zur Fehlerbehandlung The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair. Douglas Adams [Ada92] Vergleiche dies mit dem Fehlschlagen einer Vorbedingung einer Methode. (C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 974