1. Mehrfacher catch: unterschiedliche Ausnahmen behandeln
In realen Programmen kann derselbe Codeblock unterschiedliche Arten von Ausnahmen auslösen. Beim Lesen einer Datei kann zum Beispiel sowohl eine FileNotFoundException als auch eine IOException auftreten; beim Parsen von Daten außerdem eine NumberFormatException. Um jede Situation korrekt zu behandeln, können in Java mehrere catch-Blöcke hintereinander verwendet werden.
Syntax
try {
// riskante Operationen
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden: " + e.getMessage());
} catch (IOException e) {
System.out.println("Ein-/Ausgabe-Fehler: " + e.getMessage());
} catch (NumberFormatException e) {
System.out.println("Ungültiges Zahlenformat: " + e.getMessage());
}
Wichtiger Punkt:
Die catch-Blöcke müssen von spezifischeren zu allgemeineren gehen! Wenn man zuerst catch (Exception e) schreibt, werden alle übrigen Blöcke unerreichbar und der Compiler meldet einen Fehler.
Warum ist das wichtig?
- Spezifische Behandlung: Auf unterschiedliche Fehler kann unterschiedlich reagiert werden (z. B. den Nutzer eine andere Datei auswählen lassen oder die Eingabe wiederholen).
- Lesbarkeit: Der Code wird verständlicher – man sieht, welche Fehler erwartet werden und wie sie behandelt werden.
2. catch mit mehreren Typen (Multi-Catch)
Manchmal benötigen verschiedene Ausnahmen die gleiche Behandlung. Sie möchten z. B. unabhängig davon, ob es ein Dateilesefehler oder eine Zahlenkonvertierung war, einfach eine Fehlermeldung ausgeben oder sie protokollieren.
Seit Java 7 gibt es die Multi-Catch-Syntax, die es erlaubt, mehrere Ausnahmetypen in einem Block zu vereinen:
try {
// riskante Operation
} catch (IOException | NumberFormatException e) {
System.out.println("Fehler bei der Verarbeitung einer Datei oder einer Zahl: " + e.getMessage());
}
So funktioniert es:
- Die Ausnahmetypen werden durch einen senkrechten Strich (|) getrennt angegeben.
- Die Variable e hat den Typ der allgemeinsten gemeinsamen Basisklasse (meist Exception).
- Innerhalb des Blocks darf der Variablen e kein neuer Wert zugewiesen werden – sie gilt automatisch als final.
Beispiel:
try {
BufferedReader reader = new BufferedReader(new FileReader("numbers.txt"));
String line = reader.readLine();
int number = Integer.parseInt(line);
System.out.println("Zahl: " + number);
reader.close();
} catch (IOException | NumberFormatException e) {
System.out.println("Fehler: " + e.getMessage());
}
3. Best Practices für die Ausnahmebehandlung
Unterdrücken Sie keine Ausnahmen
Schlecht:
try {
// etwas Riskantes
} catch (Exception e) {
// nichts tun!
}
Warum ist das schlecht?
Ein leerer catch-Block „verschluckt“ den Fehler – Sie verlieren Informationen über die Ursache des Ausfalls, und das Programm lässt sich nicht mehr debuggen.
Besser: Geben Sie zumindest eine Meldung aus oder protokollieren Sie den Fehler.
catch (Exception e) {
e.printStackTrace();
}
Wenn Sie die Ausnahme nicht behandeln können – werfen Sie sie weiter:
catch (IOException e) {
throw e; // oder throw new RuntimeException(e);
}
Werfen Sie möglichst spezifische Ausnahmen
Wenn Sie eigene Methoden oder APIs schreiben, werfen Sie stets möglichst konkrete Ausnahmen und nicht einfach Exception oder RuntimeException. Das macht Ihren Code und Ihre Schnittstelle für andere Entwickler verständlicher.
Beispiel:
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Unzureichendes Guthaben");
}
// ...
}
Verwenden Sie Ausnahmen nicht zur Steuerung des Programmflusses
Ausnahmen sind für die Behandlung von Ausnahmefällen gedacht – also Fehlern, die im normalen Programmablauf nicht auftreten sollten. Verwenden Sie sie nicht für gewöhnliche Logik (z. B. zum Verlassen einer Schleife oder zum Prüfen von Bedingungen).
Schlecht:
try {
while (true) {
String line = reader.readLine();
if (line == null) break;
// Zeile verarbeiten
}
} catch (Exception e) {
// Ausnahme zum Verlassen der Schleife verwenden – schlecht!
}
Gut:
String line;
while ((line = reader.readLine()) != null) {
// Zeile verarbeiten
}
4. Praxisbeispiele
Beispiel: unterschiedliche Fehler unterschiedlich behandeln
try {
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
int number = Integer.parseInt(line);
System.out.println("Zahl: " + number);
reader.close();
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden!");
} catch (IOException e) {
System.out.println("Fehler beim Lesen der Datei!");
} catch (NumberFormatException e) {
System.out.println("Keine Zahl in der Datei!");
}
Beispiel: Multi-Catch für identische Behandlung
try {
// etwas Riskantes
} catch (IOException | NumberFormatException e) {
System.out.println("Fehler: " + e.getMessage());
}
5. Wichtige Feinheiten und typische Fehler
Fehler Nr. 1: Falsche Reihenfolge der catch-Blöcke. Zuerst müssen spezifischere Ausnahmen stehen und danach die allgemeinen. Andernfalls wird der breit gefasste Block nie erreicht.
Fehler Nr. 2: Verwandte Typen im Multi-Catch. Zum Beispiel catch (IOException | Exception e) wird nicht kompiliert, weil IOException von Exception erbt.
Fehler Nr. 3: Versuch, die Variable e im Multi-Catch zu ändern. In diesem Fall gilt e als final – ihr kann kein neuer Wert zugewiesen werden.
Fehler Nr. 4: Leere catch-Blöcke. Das Ignorieren von Fehlern führt zu „stillen“ Fehlern. Mindestens sollten Sie eine Meldung ausgeben oder die Ausnahme protokollieren.
Fehler Nr. 5: Alles pauschal mit Exception abfangen. Die Verwendung von catch (Exception e) verdeckt reale Probleme und erschwert das Debuggen. Fangen Sie besser nur erwartete Ausnahmen ab.
GO TO FULL VERSION