5.6.2 | catch |
Die eigentliche Fehlerbehandlung erfolgt mit einer oder mehreren catch-Anweisungen. Die catch-Anweisungen sind optional und müssen nicht angegeben werden. Falls eine Exception auftritt und keine oder keine passende catch-Anweisung vorhanden ist, dann wird die Exception an den nächsthöheren Programmblock weitergeleitet. Bei der Syntax von catch ist zu beachten, dass dem Schlüsselwort catch eine Art Parameter-Deklaration folgt. In runden Klammern wird ein Verweis auf diejenige Exception definiert, die in der betreffenden catch-Anweisung behandelt werden soll:int a, b, c; // Eingabe von b und c ... try { a = b / c; } catch(ArithmeticException e) { System.out.println( "Bitte anderen Divisor eingeben!"); System.out.println("Meldung: "+e.getMessage()); }Seit Version 1.1 ist es möglich, das Argument der catch-Anweisung mit dem Modifier final zu versehen. Hierdurch darf die übergebene Referenz innerhalb der catch-Anweisung nicht verändert werden. Datenelemente des Exception-Objekts dürfen dagegen wie sonst auch manipuliert werden.catch(final ArithmeticException e) { // Referenz e ist fix }
Die catch-Anweisung bekommt zur Laufzeit ein Exemplar der betreffenden Exception übergeben. Über den Verweis auf das Exemplar kann dann gegebenenfalls auf Datenelemente und Methoden der Exception zugegriffen werden. Eine dieser Methoden ist getMessage(), die die Fehlermeldung der Exception zurückgibt. Sie wird von der Klasse Throwable definiert und ist somit bei allen Exception-Objekten verfügbar. Bei einer Nulldivision erzeugt das Beispiel folgende Ausgabe:Bitte anderen Divisor eingeben! Meldung: / by zero
Falls innerhalb des try-Anweisungsblocks mehrere verschiedene Exceptions ausgelöst werden können, ist es möglich, auch mehrere catch-Anweisungen zu benutzen. Beim Auftreten einer Exception werden die catch-Anweisungen dann sequenziell abgeprüft, ob sie für diese Exception zuständig sind. Wenn keine entsprechende catch-Anweisung gefunden wird, dann wird die Exception an den nächsthöheren Programmblock weitergeleitet. Auf die Weiterleitung von Exceptions wird im Abschnitt 5.4 näher eingegangen. In dem folgenden Beispiel wird der String s zunächst als int ausgewertet, danach wird mit dieser Zahl ein Array indiziert. Hierbei sind folgende Fehler möglich:Beide Exceptions werden durch eigene catch-Anweisungen abgefangen:
- s kann eine Zeichenkette enthalten, die keine Ziffer darstellt. In diesem Fall wird eine NumberFormatException ausgelöst.
- Falls s eine gültige Zeichenkette enthält, ist es möglich, dass die resultierende Zahl negativ oder zu groß ist. In diesem Fall löst die zweite Anweisung im try-Block eine ArrayIndexOutOfBoundsException aus.
String s; int[] intArray = {1, 2, 3, 4}; int arrayIndex, elem; // Zuweisung an s; ... try { arrayIndex = Integer.parseInt(s); elem = intArray[arrayIndex]; } catch(NumberFormatException e) { System.out.println( "s enthält ungültige Zeichen."); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Ungültiger Indexwert."); }Bei der Verwendung mehrerer catch-Klauseln muss unbedingt darauf geachtet werden, ob eine Exception (indirekte) Oberklasse einer anderen abgefangenen Exception ist. Wenn das der Fall ist, müssen die zugehörigen catch-Anweisungen in der umgekehrten Reihenfolge der Abstammung der Exceptions angegeben werden. Das heißt, dass eine Exception erst nach den catch-Anweisungen aller ihrer Unterklassen abgefangen werden darf. Andernfalls liefert der Compiler eine Fehlermeldung. Dieses Verhalten hat folgenden Grund:
Nach den Regeln der Zuweisungskompatibilität können an einen Verweis auch Objekte aller Unterklassen zugewiesen werden. Daher fängt eine catch-Anweisung nicht nur die Exceptions der angegebenen Klasse ab, sondern auch die aller Unterklassen.
Im folgenden Beispiel ist ArrayIndexOutOfBoundsException eine Unterklasse von IndexOutOfBoundsException. Daher wird das Auftreten der ArrayIndexOutOfBoundsException bereits mit der ersten catch-Anweisung abgefangen. Die zweite catch-Anweisung ist somit unerreichbar und erzeugt eine entsprechende Fehlermeldung des Compilers.int[] intArray = {1, 2, 3, 4}; int arrayIndex = -1; try { intArray[arrayIndex] = 0; } catch (IndexOutOfBoundsException e) { System.out.println("Ungültiger Index"); } catch (ArrayIndexOutOfBoundsException e) { // Diese Stelle wird nie erreicht => Fehlermeldung System.out.println("Unerreicht"); }
Beim Abbruch des Programms durch den Interpreter wird ausgegeben, durch welche Methoden sich die Exception durchgehangelt hat, bis sie schließlich im Hauptprogramm angekommen ist. Diese Ausgabe erfolgt mit der Methode printStackTrace(), die bei allen Exceptions verfügbar ist. Von dieser Methode gibt es auch Varianten, die diese Informationen in einen anwenderdefinierten Stream schreiben.
Allen Varianten gemeinsam ist, dass sie sich der in Version 1.4 eingeführten Funktionalität zum Zugriff auf den Aufruf-Stack bedienen. Kern dieser Neuerung ist die Klasse StackTraceElement und die Methode getStackTrace() in der Klasse Throwable. Diese Methode liefert ein Array von StackTraceElement-Exemplaren, das den Aufruf-Stack der Methoden zum Zeitpunkt der Auslösung der Exception darstellt.
Anhand dieser Informationen könnte ein Programm analysieren, wo eine Exception letztlich ausgelöst wurde, und abhängig vom Auslösungsort entsprechende Maßnahmen ergreifen. Das folgende Beispiel zeigt, wie prinzipiell auf die Stack-Trace-Informationen zugegriffen wird:public class StackTraceTest { static int test() { int arrayIndex = -1; int[] intArray = {1, 2, 3, 4}; return intArray[arrayIndex]; } static void dumpStackTrace(Exception e) { // Stack-Informationen abrufen StackTraceElement[] stackTrace = e.getStackTrace(); System.err.println(e.getClass().getName()+":"); //Stack-Elemente ausgeben for(int i = 0; i < stackTrace.length; i++) System.err.println(stackTrace[i].getClassName()+"."+ stackTrace[i].getMethodName()+"() in "+ stackTrace[i].getFileName()+", line "+ stackTrace[i].getLineNumber()); } public static void main(String[] args) { try { int x = test(); } catch(Exception e) { dumpStackTrace(e); } } }Die Ausgabe dieses Programms sieht folgendermaßen aus:java.lang.ArrayIndexOutOfBoundsException: StackTraceTest.test() in StackTraceTest.java, line 8 StackTraceTest.main() in StackTraceTest.java, line 27