Ausnahmen und deren Behandlung
OOPM, Ralf Lämmel
Schon mal einen Blue
Screen gesehen?
Beispiele für Ausnahmesituationen
Programmfehler
“precondition failed”
Unzulässige Nutzereingabe
“bad url”
Nichtverfügbarkeit von Ressourcen
“connection lost”
Verausgabung von Ressourcen
“stack overflow”
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Verhalten eines Programmes bei unzulässigen Eingaben
3
$ 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?
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Quellen für Ausnahmesituationen im Programm
5 public class Program {
public static void main(String[] args) { int value1 = Integer.parseInt(args[0]);
int value2 = Integer.parseInt(args[1]);
int sum = value1 + value2;
System.out.println(value1 + " + " + value2 + " = " + sum);
} }
Feldzugriff kann scheitern
Konvertierung in Zahl kann scheitern
Illustrationen für Ausnahmebehandlung siehe Repository: idioms.errorhandling
Haben Sie noch eine weitere Idee
für eine Ausnahmesituation?
Ansatz der Ausnahmebehandlung
public class Program {
public static void main(String[] args) { int value1 = Integer.parseInt(args[0]);
int value2 = Integer.parseInt(args[1]);
int sum = value1 + value2;
System.out.println(value1 + " + " + value2 + " = " + sum);
} }
Teste dass args.length == 2
Behandle nichterfolgreiche Konvertierung
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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?
7
Fehler
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.
Der Begriff ist
hochgradig überladen.
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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
9
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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
11
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;
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);
}
Abfrage des Fehlerflags nach
jedem Aufruf
Das Abfragen des Flags kann leicht
vergessen werden.
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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
13
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;
if (!error) {
int sum = value1 + value2;
System.out.println(value1 + " + " + value2 + " = " + sum);
}
else {
System.err.println("Argument(s) not a number");
System.exit(-1);
}
Interpretiere null als Fehler
Fehler können weiterhin übersehen werden oder der Fehlerwert kann fehlinterpretiert werden. Auch verlangt
diese Technik häufig Typänderungen.
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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
15
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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
17
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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.
19
Vom Programm geworfene Ausnahme
public class Program {
public static void main(String[] args) {
throw new RuntimeException("Don’t call main!");
}
} Ein konkreter
Ausnahmetyp
Parameter für
Diagnose.
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Vordefinierte Ausnahmeklassen
21
Ausnahmen werden durch Klassen in einer
Vererbungshierarchie
organisiert.
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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.
....
23
Sprachunterstützung für Ausnahmebehandlung
try {
... // Anweisungen die Ausnahmen werfen könnten }
catch(AusnahmeTyp name) {
... // Anweisungen welche die Ausnahme behandeln
}
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Ausnahmebehandlung mit try/catch
25
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);
}
Sprachunterstützung für Ausnahmebehandlung einschließlich des finally-Blockes
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.
try {
... // Anweisungen
} catch (AusnahmeTyp name) { ... // Anweisungen
} finally {
... // Anweisungen }
try-Block
finally- Block
catch- Block
Keine Ausnahme
Ausnahme
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Mehrere Catch-Klauseln
27
try {
} catch (AusnahmeTyp1 name1) { …
…
} catch (AusnahmeTyp2 name2) {
…
} finally {
… }
Klauseln werden in Definitionsreihefolge geprüft.
Speziellere Ausnahmetypen sind zuerst anzugeben!
Ein Tabu-Idiom
Konsumiert alle Ausnahmen.
Behandelt keine Ausnahmen.
Verursacht eventuell schwierige Folgefehler.
try { ...
}
catch (Exception x) { }
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Propagierung von Ausnahmen
Innerhalb von Hierarchien von Throw-Blöcken zum unmittelbar umfassenden throw-Block Im Aufrufkeller
zum nächsten Aufruf mit einem Handler
29
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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)
“Stack trace”
31
Ausnahmetyp Parameter
der Ausnahme
Methode der
Behandlung
Aufrufkeller und Ausnahmebehandlung (für ein neues Szenario)
main
Methode mit Exception handler
Methode ohne Exception handler
Methode mit Ausnahme
Methodenaufruf Methodenaufruf Methodenaufruf
Methode wirft Ausnahme
Methode reicht Ausnahme durch
Methode behandelt Ausnahme
Methode ist
“unbehelligt”
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
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.
33
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Ein weiteres Beispiel:
Abhebung vom Konto
Ausnahmesituationen Betrag ist negativ
IllegalArgumentException Betrag überzieht Konto
OverdraftException
35
Anwendungsspezifisches Problem, welches behandelt werden sollte.
Grundlegendes Problem (als Folge eines Bedienfehlers oder
einer Vertragsverletzung)
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) {
// Precondition if (amount < 0)
throw new IllegalArgumentException();
if (balance < amount)
throw new OverdraftException();
balance -= amount;
}
Vordefinierte Ausnahmeklasse
Programm-definierte
Ausnahmeklasse
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Programm-definierte Ausnahmenklassen
37
Unterklassen von Exception Konstruktoren
Standardkonstruktor
Konstruktor mit String-Parameter (“Fehlermeldung”) Weiterer Zustand
Daten für Handler
public class OverdraftException
extends RuntimeException { }
Für
fortgeschrittene
Szenarien
Verwendung von
Zusicherungen vs. Ausnahmen
public void withdraw(float amount) { // Precondition
assert !( amount < 0
|| balance < amount);
balance -= amount;
}
Verletzten der
Vorbedingung führt zum Werfen einer Ausnahme
AssertionError.
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Verwendung von
Zusicherungen vs. Ausnahmen
39
public void withdraw(float amount) {
// Precondition if (amount < 0)
throw new IllegalArgumentException();
if (balance < amount)
throw new OverdraftException();
balance -= amount;
}
Spezifischere Ausnahme als AssertionError
Wir werden zeigen, dass wir den Aufrufer zur Behandlung
zwingen wollen und können!
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Wir wollen dieses Programm verbieten.
41
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.
Verwendung geprüfter Ausnahmen
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
public class OverdraftException
extends RuntimeException { }
Änderung unserer
ursprünglichen
Ausnahmeklasse
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Dieses Programm wird abgewiesen.
43
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.
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) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Die Behandlungspflicht ergeht an Aufrufer.
45
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.
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)
throw new IllegalArgumentException();
if (balance < amount)
throw new OverdraftException();
balance -= amount;
}
Der Compiler (das Typsystem) verlangt die throws-Deklaration (im Falle einer geprüften
IllegalArgumentExceptio n ist eine ungeprüfte Ausnahmeklasse und muss deswegen nicht
deklariert werden.
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau
Geprüft oder ungeprüft?
47
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.
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.
Quelle:
Douglas Adams [Ada92]
(C) 2007-2018, Ralf Lämmel, Universität Koblenz-Landau