8 Ausnahmen müssen sein
»›Wir sind in Sicherheit! Er kann uns nicht erreichen!‹
›Sicher?‹
›Ganz sicher! Bären haben Angst vor Treibsand!‹«
– Hägar, Dik Browne (1917–1989)
Fehler beim Programmieren sind unvermeidlich. Schwierigkeiten bereiten nur die unkalkulierbaren Situationen – hier ist der Umgang mit Fehlern ganz besonders heikel. Java bietet die elegante Methode der Exceptions, um mit Fehlern flexibel umzugehen.
8.1 Problembereiche einzäunen
In den frühen Programmiersprachen gab es für Routinen keine andere Möglichkeit, als über den Rückgabewert einen Fehlschlag anzuzeigen – in der Programmiersprache C ist das auch heute noch der Fall. Der Fehlercode ist häufig -1, aber auch NULL oder 0. Allerdings kann die Null auch Korrektheit anzeigen. Irgendwie ist das willkürlich. Die Abfrage dieser Werte ist unschön und wird von uns gern unterlassen, zumal wir oft davon ausgehen, dass ein Fehler in dieser Situation gar nicht auftreten kann – diese Annahme kann aber eine Dummheit sein. Zudem wird der Programmfluss durch Abfragen der Rückgabeergebnisse unangenehm unterbrochen, zumal der Rückgabewert, wenn er nicht gerade einen Fehler anzeigt, weiterverwendet wird. Der Rückgabewert ist also im weitesten Sinne überladen, da er zwei Zustände anzeigt. Häufig entstehen mit den Fehlerabfragen kaskadierte if-Abfragen, die den Quellcode schwer lesbar machen.
[zB] Beispiel
Die Java-Bibliothek geht bei den Methoden delete(), mkdir(), mkdirs() und renameTo(…) der Klasse File nicht mit gutem Beispiel voran. Anstatt über eine Ausnahme anzuzeigen, dass die Operation nicht geglückt ist, liefern die genannten Methoden false. Das ist unglücklich, denn viele Entwickler verzichten auf den Test, und so entstehen Fehler, die später schwer zu finden sind.
8.1.1 Exceptions in Java mit try und catch
Bei der Verwendung von Exceptions wird der Programmcode nicht durch Abfrage des Rückgabestatus unterbrochen. Ein besonders ausgezeichnetes Programmstück überwacht die ordentliche Ausführung und ruft im Fehlerfall speziellen Programmcode zur Behandlung auf.
Den überwachten Programmbereich (Block) leitet das Schlüsselwort try ein. Dem try-Block folgt in der Regel[ 179 ](In manchen Fällen auch ein finally-Block, sodass es dann ein try-finally wird. ) ein catch-Block, in dem Programmcode steht, der die Ausnahme behandelt. Kurz skizziert, sieht das so aus:
try {
// Programmcode, der eine Ausnahme ausführen kann
}
catch ( ... ) {
// Programmcode zum Behandeln der Ausnahme
}
// Es geht ganz normal weiter, denn die Ausnahme wurde behandelt
Fehler führen zu Ausnahmen, und diese Ausnahmen behandelt ein catch-Block. Hinter catch folgt also der Programmblock, der beim Auftreten einer Ausnahme ausgeführt wird, um den Fehler abzufangen (daher der Ausdruck catch). Der Fehler kann auf der Kommandozeile gemeldet oder etwa in einen Logger geschrieben werden. Ob es zur Laufzeit wirklich zu einem Fehler kommt, ist nicht bekannt, aber wenn, dann ist eine Behandlung vorhanden.
Es ist nach der Fehlerbehandlung nicht mehr so einfach möglich, an der Stelle fortzufahren, an der der Fehler auftrat. Auch lässt sich im Nachhinein nicht wirklich feststellen, an welcher Stelle genau der Fehler aufgetreten ist, wenn es in einem großen try-Block mehrere ausnahmenauslösende Stellen gibt. Andere Programmiersprachen erlauben das durchaus.
8.1.2 Geprüfte und ungeprüfte Ausnahmen
Java unterscheidet zwischen zwei Gruppen von Ausnahmen: geprüften und ungeprüften Ausnahmen. Die geprüften Ausnahmen müssen zwingend behandelt werden, indem sie entweder aufgefangen oder weitergeleitet werden. Sie finden sich der Regel bei Ein-/Ausgabe-Operationen. Ungeprüfte Ausnahmen müssen nicht unbedingt aufgefangen werden. Treten die Ausnahmen jedoch auf und werden sie nicht aufgefangen, so führt das zum Ende des ausführendes Threads. In den folgenden zwei Abschnitten betrachten wir diese Ausnahmen im Detail.