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
Abarbeitung mehrerer catch-Anweisungen
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:
- 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.
Beide Exceptions werden durch eigene catch-Anweisungen abgefangen:
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");
}
Ausgabe und Analyse der Aufrufhierarchie
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
Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.