Programmierung
Michael Goedicke
[email protected] auf der Basis von Folien von V Gruhn
Ausnahmen (Exceptions)
im Programmablauf konnen Ausnahmesituationen auftreten z.B. Division durch Null, Datei nicht vorhanden,
Ubergabe eines ungultigen Parameters an eine Methode
allgemein: Zustand, der das Programm daran hindert, im normalen Ablauf
fortzufahren
Werden solche Situationen nicht vom Programmierer vorhergesehen,
gerat das Programm evtl. in nicht definierte Zustande, bricht ab o.a.
Idealerweise werden solche Ausnahmesituationen durch umsichtige Programmierung von vornherein vermieden.
Manchmal ist es aber unmoglich (insb. bei externen Ereignissen) oder sehr umstandlich, Ausnahmesituationen zu vermeiden.
U.u. ist es dann einfacher, Ausnahmesituationen eintreten zu lassen und sie ordentlich zu behandeln, als sie unbedingt zu vermeiden.
Exception Handling
M. Goedicke Programmierung WiSe 2020/2021 2
Ausnahmebehandlung: Motivation
Naives Vorgehen zur Ausnahmesignalisierung in einer Methode: bestimmter Ruckgabewert, der als Fehlersignal vereinbart wird
Bsp.: Wir wollen vermeiden, dass bei Initialisierung eines Buch-Objekts das Erscheinungsjahr vor Erfindung des Buchdrucks gesetzt wird:
public class Buch {
private int jahr; // Erscheinungsjahr
public boolean setJahr (int jahr) {
if (jahr > 1452) {
this.jahr = jahr;
return true; // Signal gultiger Parameter
} else {
return false; // Signal ungultiger Parameter }
}
Problem: Im aufrufenden Programmteil ist eine umstandliche Prufung des Erfolgs erforderlich.
M. Goedicke Programmierung WiSe 2020/2021 3
Ausnahmebehandlung: Motivation
Besonders problematisch, wenn eine Methode eigentlich kein Erfolgs- / Fehlersignal liefern sollte, sondern einen fachlichen Ruckgabewert:
public class Bibliothek {
public int zahlDerNeuerscheinungen (int jahr) { if (jahr > 1452) {
// zaehle in jahr erschienene Bucher
return gefundeneAnzahl; }
else {
return -1; // Signal ungultiger Parameter
} }
mogliche Ruckgabewerte der Methode
0n Zahl der gefundenen Bucher (bei gultigem Parameter) -1 Fehlersignal (bei ungultigem Parameter)
M. Goedicke Programmierung WiSe 2020/2021 4
Ausnahmebehandlung: Motivation
Umstandliche Uberprufung des Ruckgabewerts der Methode auf das vereinbarte Fehlersignal in der aufrufenden Methode:
int anzahl = bib.zahlDerNeuerscheinungen(suchJahr); switch (anzahl) {
case 1:
System.out.println (Ungultiges Suchjahr.); break;
case 0:
System.out.println (Keine Buecher gefunden.); break;
default:
System.out.println (anzahl + Bucher gefunden.); System.out.println (Buchliste anzeigen?);
}
M. Goedicke Programmierung WiSe 2020/2021 5
Ausnahmebehandlung: Motivation
Fehlersignalisierung uber den Methoden-Ruckgabewert insb. dann praktisch unmoglich, wenn der komplette Ruckgabe-Wertebereich fachlich potentiell erreichbar ist, also kein Randbereich fur die Fehlersignalisierung bleibt.
Beispiel: Die Methode soll den Kontostand zum Ende des ubergebenen Jahrs zuruckliefern:
public double getAbschlussSaldo(int jahr) { }
Problem: Beliebige positive und negative Werte sowie 0 sind als gultige Ruckgabewerte moglich. Wie signalisieren wir dann die Ausnahmesituation, dass das Konto im angefragten Jahr noch gar nicht existierte?
M. Goedicke Programmierung WiSe 2020/2021 6
Ausnahmebehandlung: Motivation
Probleme bei der Ausnahmebehandlung uber besondere Ruckgabewerte:
Fehler werden auf dem gleichen Weg zuruckgegeben wie regulare Ruckgabewerte
Ruckgabewert von Methoden ist aber meistens fachlich bestimmt und nicht gut zur Fehlersignalisierung geeignet
Programmierer versuchen, Fehlersignale in Randbereichen des fachlichen Wertebereichs unterzubringen
Nicht immer ist ein Randbereich dafur verfugbar
Bedeutung so codierter Fehlersignale muss vereinbart werden wenn uberhaupt, passiert das nur informell in Kommentaren
anfallig fur Missverstandnisse bei der Entwicklung im Team
Gefahr von falsch oder gar nicht behandelten Fehlercodes Seiteneffekte, Programmabbruche,
M. Goedicke Programmierung WiSe 2020/2021 7
Ausnahmebehandlung: Exceptions als Losung
eigener Kanal fur Signalisierung von Fehlern
Entkopplung der Signale vom regularen Ruckgabewert
eigene Datenstrukturen fur Fehlersignale differenzierte, genaue Fehlermeldungen
formelle Deklaration moglicher Fehlersignale
keine Gefahr von Missverstandnissen bei der Signalvereinbarung
erzwungene Behandlung von Fehlersignalen
keine vernachlassigten oder ubersehenen Fehler
Robustere, lesbarere Programme
M. Goedicke Programmierung WiSe 2020/2021 8
Exceptions
Fehlersignale in Java: Exceptions
von Exception abgeleitete Klassen
enthalten Methoden zur Beschreibung der Fehlersituation
von Java verwendete Exceptions (kleiner Auszug)
ArithmeticException, ArrayIndexOutOfBoundsException,
IllegalArgumentException, NullPointerException, NegativeArraySizeException, FileNotFoundException, MalformedURLException, UnknownHostException u.v.m.
selbstdefinierte Unterklassen von Exception
zur Signalisierung von Fehlersituationen in der eigenen Anwendung, z.B.
UngueltigesJahrException, BuchBereitsVerliehenException,
Konvention: Exceptions werden mit Suffix Exception benannt
M. Goedicke Programmierung WiSe 2020/2021 9
Eigene Exception-Klassen
Ableitung eigener Exceptions von Oberklasse Exception
Deklaration eines leeren Konstruktors und eines Konstruktors mit String-
Parameter, um Exceptions beim Werfen erzeugen zu konnen
public class UngueltigesJahrException extends Exception { public UngueltigesJahrException() {
super(); }
public UngueltigesJahrException(String message) { super(message);
} }
Erinnerung: Konstruktoren
Bei der Ableitung werden Konstruktoren nicht mitvererbt, darum mussen
wir sie selbst deklarieren
Wenn wir einen parametrisierten Konstruktor deklarieren (hier: zur
Ubergabe einer Fehlererlauterung), erzeugt Java keinen impliziten leeren Konstruktor, wir mussen ihn darum selbst deklarieren
M. Goedicke Programmierung WiSe 2020/2021 10
Deklarieren und Werfen von Exceptions
Statt Ruckgabe eines speziell zu vereinbarenden Ruckgabewerts: Signalisieren des Fehlers durch Werfen einer Exception mit throw
public class Bibliothek {
public int zahlDerNeuerscheinungen (int jahr)
throws UngueltigesJahrException {
if (jahr > 1452) {
// in jahr erschienene Bucher zaehlen return gefundeneAnzahl; // Wert zuruckgeben
}
else {
throw new UngueltigesJahrException();
}
}
}
Statt informeller Dokumentation des Fehlersignals z.B. in einem Kommentar, der vergessen/ubersehen werden kann:
Formale Deklaration der Exceptions, die die Methode im Fehlerfall werfen kann, mit throws im Methodenkopf
M. Goedicke Programmierung WiSe 2020/2021 11
Fangen von Exceptions: Deklaration
In der aufrufenden Methode: Eingrenzen des Programmteils, von dem Exceptions geworfen werden konnen: try-Block
try {
int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); if (anzahlNeue == 0)
System.out.println (Keine Buecher gefunden.);
else {
System.out.println (anzahlNeue + Buecher gefunden.); System.out.println (Buchliste anzeigen?);
} }
catch (UngueltigesJahrException e) { System.out.println (Ungultiges Suchjahr.);
}
Abfangen und Behandeln der Exception: catch-Block
M. Goedicke Programmierung WiSe 2020/2021 12
Fangen von Exceptions: Ablauf
wenn Exception auftritt, wird die Bearbeitung des try-Blocks abgebrochen und mit catch-Block fortgefahren
try {
int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); if (anzahlNeue == 0)
System.out.println (Keine Buecher gefunden.);
else {
System.out.println (anzahlNeue + Buecher gefunden.); System.out.println (Buchliste anzeigen?);
} }
catch (UngueltigesJahrException e) { System.out.println (Ungultiges Suchjahr.);
}
wenn keine Exception, wird catch-Block ubersprungen
M. Goedicke Programmierung WiSe 2020/2021 13
Behandlungsvarianten
mit catch fangen und behandeln
haufigste Form, wie gerade gezeigt
mit catch fangen und eine andere Ausnahme werfen
z.B., um technische Fehlersignale in fachliche zu verwandeln
und sie auf hoherer Ebene behandeln zu lassen
nicht fangen, sondern weiterfliegen lassen Fehlersignal nicht selbst behandeln,
sondern von hoherer Ebene behandeln lassen
aber niemals: fangen und nichts tun
catch (Exception e) {} // niemals verwenden!
auftretende Fehler wurden ignoriert und versteckt
zumindest den Fehler in Logdatei o.a. protokollieren!
M. Goedicke Programmierung WiSe 2020/2021 14
Fangen und andere Exception werfen
z.B. um technische Fehlersignale in fachliche zu verwandeln
public class Bibliothek {
private Buch[] buchBestand;
private int index = 0;
public Buch naechstesBuch () throws ListenEndeException
{
try {
index++;
return buchBestand[index]; }
catch (ArrayIndexOutOfBoundsException e) { throw new ListenEndeException();
} }
}
Aufrufer von naechstesBuch muss sich zwar um
ListenEndeException kummern, muss aber nicht wissen, dass der Buchbestand als Array implementiert ist
M. Goedicke Programmierung WiSe 2020/2021 15
Exception weiterfliegen lassen
um sie von hoherer Ebene behandeln zu lassen public class Bibliothek {
private Buch[] buchBestand;
private int index = 0;
public Buch naechstesBuch ()
throws ArrayIndexOutOfBoundsException {
index++;
return buchBestand[index]; }
}
Arrayzugriff kann ArrayIndexOutOfBoundsException erzeugen,
die aber hier nicht gefangen wird
nicht gefangene Exceptions werden an aufrufende Methode
weitergegeben
Deklaration im throws-Teil des Methodenkopfes
M. Goedicke Programmierung WiSe 2020/2021 16
Exception-Behandlung durch JVM
wenn eine Exception nirgends gefangen, sondern immer weiter nach oben durchgereicht wird, behandelt die Java Virtual Machine sie schlielich durch Programmabbruch und Fehlerausgabe:
Exception in thread main
java.lang.ArrayIndexOutOfBoundsException: 5
at Bibliothek.naechstesBuch(Bibliothek.java:8) at Bibliothek.main(Bibliothek.java:13)
Ausgabe der Exception mit Stack Trace Thread-Name
Name der Exception
ggf. Informationen zum Grund des Fehlers
Call Stack (Aufrufhierarchie der Methode, die Exception erzeugte)
mit Zeilennummern
Hilfreich fur den Entwickler zur zielgerichteten Eingrenzung des
Fehlers, aber fatal fur den Anwender (Programmabbruch!)
M. Goedicke Programmierung WiSe 2020/2021 17
Optional: finally-Block
Gelegentlich Aufraumarbeiten wie Schlieen von Dateien notig, unabhangig davon, ob Exception auftrat oder nicht
Deklaration in finally-Block nach letztem catch-Block
Wird grundsatzlich nach try- bzw. catch-Block ausgefuhrt
Auch, wenn im try/catch-Block return oder throw o.a. steht, d.h.
der Programmcode danach eigentlich gar nicht mehr erreicht wurde
try {
// Anweisungen
}
catch (ArrayIndexOutOfBoundsExceptione) {
// Ausnahmebehandlung
throw new ListenEndeException(); }
finally {
// Aufraumarbeiten
}
aufrufender Programmteil
M. Goedicke Programmierung WiSe 2020/2021
18
Exceptions in Konstruktoren
Beispiel: Initialisierung von Buch-Objekten im Konstruktor:
public class Buch {
public Buch (String autor, String titel,
String verlag, int jahr)
{ } }
Motivation:
Ein Konstruktor hat keinen deklarierten Ruckgabewert (bzw. sein
Ruckgabewert ist immer eine Instanz der jeweiligen Klasse), d.h. Exceptions sind unsere einzige Moglichkeit, Fehlersignale an den aufrufen Programmteil zu melden.
Wenn ein Konstruktor normal beendet wird, gibt er immer eine Instanz der jeweiligen Klasse zuruck. Das Werfen einer Exception ist die einzige Moglichkeit, ein Konstruktur ohne Anlegen einer Instanz zu beenden (z.B. wenn es aufgrund fehlerhafter Paramter nicht moglich ist, ein sinnvoll initialisiertes Objekt zu erzeugen).
M. Goedicke Programmierung WiSe 2020/2021 19
Werfen mehrerer Exceptions
komma-getrennte Deklaration moglicher Exceptions im Kopf (in beliebigen Methoden moglich, nicht nur in Konstruktoren)
public class Buch {
public Buch (String autor, String titel, String verlag, int jahr)
throws KeinTitelException, UngueltigesJahrException
{
this.autor = autor; if (titel != )
this.titel = titel; else
throw new KeinTitelException();
this.verlag = verlag;
if (jahr > 1452)
this.jahr = jahr; else
throw new UngueltigesJahrException(); }
}
M. Goedicke Programmierung WiSe 2020/2021 20
Fangen mehrerer Exceptions
mehrere catch-Blocke hintereinander
try {
neuesBuch = new Buch (neuAutoren, neuTitel, neuVerlag, neuJahr);
}
catch (KeinTitelException e1) {
System.out.println (Kein Titel angegeben.);
}
catch (UngueltigesJahrException e2) { System.out.println (Ungultiges Erscheinungsjahr.);
}
wenn Exception auftritt, wird der passende catch-Block gesucht und ausgefuhrt
sonst fliegt die Exception weiter an ubergeordnete Methode
M. Goedicke Programmierung WiSe 2020/2021 21
Klassenhierarchie bei Exceptions
hierarchische Deklaration von Exceptions ublich z.B. Exception RuntimeException
IndexOutOfBoundsException ArrayIndexOutOfBoundsException
Vorsicht: catch-Blocke fur Ober-Exceptions fangen auch Unter-
Exceptions!
try {}
catch (Exception e1) {
// faengt alles
System.out.println (Ausnahmesituation);
}
catch (ArrayIndexOutOfBoundsException e2) {
// nicht erreichbar!
System.out.println (Arraygrenze ueberschritten);
}
catch-Reihenfolge von speziellen zu generischen Exceptions ordnen M. Goedicke Programmierung WiSe 2020/2021 22
Nachrichten in Exceptions speichern
Ubergabe von Informationen uber Fehlerursache im Konstruktor der Exception moglich
public class Buch {
public Buch (String autor, String titel, String verlag, int jahr)
throws UngueltigeEingabeException { this.autor = autor;
if (titel != )
this.titel = titel;
else
throw new UngueltigeEingabeException(Kein Titel); this.verlag = verlag;
if (jahr > 1452)
this.jahr = jahr;
else
throw new UngueltigeEingabeException(Jahr ungultig);
}
}
M. Goedicke Programmierung WiSe 2020/2021 23
Nachrichten aus Exceptions lesen
beim Fangen wird die gerade erzeugte Exception-Instanz der Variable im catch-Ausdruck zugewiesen
Zugriff auf die Methoden der Exception wie gewohnt per Punktnotation
try {
neuesBuch = new Buch (neuAutoren, , neuVerlag, neuJahr);
}
catch (UngueltigeEingabeException e) {
System.out.println (e.getMessage()); // Kein Titel }
Methoden (z.B. getMessage, toString) werden von Oberklasse Exception geerbt
M. Goedicke Programmierung WiSe 2020/2021 24
Checked und unchecked Exceptions
selbstdefinierte Exceptions mussen im throws-Teil des
Methodenkopfs deklariert werden
regulares Methodenverhalten aus der Signatur ablesbar
Ausnahmeverhalten der Methode aus dem throws-Teil ablesbar
sowohl fur den Programmierer als auch den Compiler
Compiler fordert, dass deklarierte Exceptions vom aufrufenden
Programmteil abgefangen werden (checked Exceptions)
einige von Java generierte Exceptions mussen nicht im throws-Teil
deklariert werden
konkret: alle von RuntimeException abgeleitete Exceptions
signalisieren Laufzeitfehler, die bei umsichtiger Programmierung
eigentlich nicht auftreten durfen
konnten aber uberall auftreten (z.B. NullPointerException), sodass
es umstandlich ware, sie uberall zu deklarieren und zu prufen
Abfangen wird daher vom Compiler nicht uberpruft (unchecked)
M. Goedicke Programmierung WiSe 2020/2021 25
Checked und unchecked Exceptions
Von RuntimeException abgeleitete Exceptions, deren Deklaration und Behandlung der Compiler nicht einfordert, sind u.a. ArithmeticException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException, NullPointerException, uva.
Sie signalisieren technische Fehler, die prinzipiell uberall auftreten konnen, bei sauberer Programmierung aber vermeidbar sind.
Nach dem Motto Manchmal ist es einfacher, Fehler zuzulassen und sie zu behandeln, als sie zu vermeiden, konnen Entwickler solche Exceptions aber auch selber werfen und fangen (z.B. die IllegalArgumentException).
Mussen im Methodenkopf nicht deklariert werden.
Checked Exceptions signalisieren hingegen Fehler, die eher fachlicher Natur sind (also z.B. von Nutzereingaben oder aktionen herruhren) und somit vom Entwickler nicht verhinderbar sind, und i.d.R. nur an bestimmten Stellen auftreten konnen.
z.B. IOException: Auf Probleme beim Dateizugriff muss ein Programm vorbereitet sein, daher erfordert der Compiler ihre Behandlung.
M. Goedicke Programmierung WiSe 2020/2021 26
Leitlinien fur den Exception-Einsatz
Beim Deklarieren individueller Checked Exceptions nicht ubertreiben
zu umstandliche erzwungene Handhabung fuhrt dazu, dass
Entwickler des aufrufenden Programmteils Abkurzungen nehmen
Todsunde: catch (Exception e) { }
Niemals einen catch-Block leer lassen! Zumindest System.out.println(e)!
Checked Exceptions (die ja irgendeine Behandlung erzwingen) nur einsetzen, wenn vom aufrufenden Programmteil auch tatsachlich eine sinnvolle Behandlung erwartet werden kann
Wenn vorauszusehen ist, dass der aufrufende Programmteil im Angesicht des betreffenden Fehlers ebenfalls machtlos sein konnte, lieber eine Unchecked Exception verwenden dann kann der aufrufende Entwickler entscheiden, ob er sich sinnvoll um den Fehler kummern kann oder die Exception nach oben weiterfliegen lasst.
Fur diese Entscheidung notwendig: Alle Exceptions, die man in einer Methode u.U. wirft, sorgfaltig kommentieren insb. unchecked Ex.
M. Goedicke Programmierung WiSe 2020/2021 27
Leitlinien fur den Exception-Einsatz
Insb. in unchecked Exceptions, die ggf. bis zur JVM hochfliegen und auf der Konsole ausgegeben werden, moglichst viel Information zur Eingrenzung des Fehlers speichern:
Beim Fangen einer Low-Level-Exception und Weiterwerfen einer High- Level-Exception: Ursprungliche Exception der neuen Exception mitgeben
Beim Erzeugen einer eigenen Exception: Aussagekraftige Fehlermeldung incl. der fehlerverursachenden Variablenwerte
Auch beim Werfen von Exceptions das betroffene Objekt nach Moglichkeit in konsistentem, gultigem Zustand zurucklassen.
Entweder Anderungen erst vornehmen, wenn sichergestellt ist, dass alle Vorbedingungen erfullt sind
oder Anderungen zurucknehmen, wenn sich herausstellt, dass nicht alle voneinander abhangigen Anderungen ausgefuhrt werden konnen.
Lasst sich nicht immer erreichen bevor ubertriebene Klimmzuge zu diesem Zweck gemacht werden, besser im Kommentar den aufrufenden Programmteil darauf hinweisen.
M. Goedicke Programmierung WiSe 2020/2021 28
Objektkonsistenz trotz Fehlersituation sicherstellen
Reaktion auf Fehler (Meldung oder Korrektur)
moglichst fruh idealerweise vor Zustandsanderung des Objekts
Objekt konnte sonst inkonsistenten Zustand annehmen Beispiel: public Date ausleihen (Benutzer nutzer) {
leihfrist = ; // Attribut leihfrist setzen if (nutzer != null)
ausleiher = nutzer // Attr. ausleiher setzen else throw new IllegalArgumentException();
}
In diesem Fall kann es passieren, dass das Attribut leihfrist zwar bereits verandert wird, dann aber kein ausleiher mehr eingetragen wird
Das Objekt wurde sich in einem inkonsistenten Zustand befinden!
M. Goedicke Programmierung WiSe 2020/2021 29
Objektkonsistenz trotz Fehlersituation sicherstellen
Besser: Prufung vor Zustandsanderung
public Date ausleihen (Benutzer nutzer) { if (nutzer == null)
throw new IllegalArgumentException();
ausleiher = nutzer; // Attribut ausleiher setzen leihfrist = new Date(); // Attr.leihfrist setzen return leihfrist;
}
Entweder werden alle Attribute konsistent zueinander verandert oder keines.
M. Goedicke Programmierung WiSe 2020/2021 30
Zusammenfassung: Exceptions werfen und fangen
Deklarieren und Werfen von Exceptions
typ methode ()
throws Exceptionklasse1, Exceptionklasse2 {
throw new Exceptionklasse1(); // oder throw new Exceptionklasse2(Nachricht);
}
Fangen und Behandeln von Exceptions
try {}
catch (Exceptionklasse1 e) {} catch (Exceptionklasse2 e) {} finally {}
M. Goedicke Programmierung WiSe 2020/2021 31
Zusammenfassung: Behandlungsvarianten
Exception mit catch fangen und behandeln
Fehler dort behandeln, wo er signalisiert worden ist
Exception mit catch fangen und andere Exception werfen z.B., um technische Fehlersignale in fachliche zu verwandeln
und sie auf hoherer Ebene behandeln zu lassen
Exception nicht fangen, sondern weiterfliegen lassen Fehlersignal nicht selbst behandeln,
sondern von hoherer Ebene behandeln lassen
M. Goedicke Programmierung WiSe 2020/2021 32
Reviews
There are no reviews yet.