Wenn etwas schiefgeht (Datei fehlt, Division durch 0, ungültige Eingabe), löst Java eine Ausnahme (Exception) aus. Sie bricht den normalen Ablauf ab und springt zum nächsten passenden catch.
public class Fangen {
public static void main(String[] args) {
try {
System.out.println("vorher");
int x = Integer.parseInt("abc");
System.out.println("wird nie erreicht " + x);
} catch (NumberFormatException e) {
System.out.println("Ungültige Zahl: " + e.getMessage());
} finally {
System.out.println("läuft immer");
}
System.out.println("Programm läuft weiter");
}
}vorher Ung?ltige Zahl: For input string: "abc" l?uft immer Programm l?uft weiter
Mehrere catch-Blöcke
public class Mehrere {
static void test(int variante) {
try {
switch (variante) {
case 0 -> System.out.println(10 / (variante));
case 1 -> { String s = null; s.length(); }
case 2 -> { int[] a = new int[2]; a[5] = 1; }
case 3 -> Integer.parseInt("x");
default -> throw new IllegalStateException("Zustand " + variante);
}
} catch (ArithmeticException | NullPointerException e) { // mehrere Typen
System.out.println("Rechen/Null: " + e.getClass().getSimpleName());
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Index: " + e.getMessage());
} catch (RuntimeException e) { // allgemeiner zuletzt
System.out.println("Sonstiges: " + e.getClass().getSimpleName() + " " + e.getMessage());
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) test(i);
}
}Rechen/Null: ArithmeticException Rechen/Null: NullPointerException Index: Index 5 out of bounds for length 2 Sonstiges: NumberFormatException For input string: "x" Sonstiges: IllegalStateException Zustand 4
Reihenfolge: spezifisch vor allgemein. Ein allgemeiner Typ (Exception) vor einem speziellen würde diesen verdecken (Compilerfehler).
Die Hierarchie
Throwable
├─ Error schwere JVM-Fehler (OutOfMemoryError, StackOverflowError), nicht fangen
└─ Exception
├─ RuntimeException ungeprüft (unchecked): Programmierfehler
│ ├─ NullPointerException, IllegalArgumentException, IllegalStateException
│ ├─ IndexOutOfBoundsException, ArithmeticException, ClassCastException
│ └─ NumberFormatException, UnsupportedOperationException
└─ andere Exceptions geprüft (checked): IOException, SQLException ...| Art | Beispiele | Pflicht? |
|---|---|---|
| Checked (geprüft) | IOException, SQLException | musst du fangen oder mit throws weitergeben |
Unchecked (RuntimeException) | NullPointerException, IllegalArgumentException | keine Pflicht |
throws und throw
import java.io.*;
public class Werfen {
static double teile(double a, double b) {
if (b == 0) throw new ArithmeticException("Division durch 0");
return a / b;
}
static String lese(String pfad) throws IOException { // checked: Aufrufer muss reagieren
try (BufferedReader r = new BufferedReader(new FileReader(pfad))) {
return r.readLine();
}
}
public static void main(String[] args) {
try {
System.out.println(teile(10, 4));
System.out.println(teile(1, 0));
} catch (ArithmeticException e) {
System.out.println("Fehler: " + e.getMessage());
}
try {
lese("gibt-es-nicht.txt");
} catch (IOException e) {
System.out.println("IO: " + e.getClass().getSimpleName());
}
}
}2.5 Fehler: Division durch 0 IO: FileNotFoundException
try-with-resources
Dateien, Verbindungen und Streams müssen geschlossen werden. Mit try (...) passiert das automatisch, auch bei Fehlern. Das Objekt muss AutoCloseable sein:
public class Ressource {
static class Verbindung implements AutoCloseable {
final String name;
Verbindung(String name) { this.name = name; System.out.println("öffne " + name); }
void sende(String s) { System.out.println(name + " sendet " + s); if (s.isEmpty()) throw new IllegalArgumentException("leer"); }
@Override public void close() { System.out.println("schließe " + name); }
}
public static void main(String[] args) {
try (Verbindung a = new Verbindung("A"); Verbindung b = new Verbindung("B")) {
a.sende("Hallo");
b.sende("");
} catch (IllegalArgumentException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}?ffne A ?ffne B A sendet Hallo B sendet schlie?e B schlie?e A Fehler: leer
Geschlossen wird in umgekehrter Reihenfolge der Öffnung.
Eigene Ausnahmen
public class Eigene {
static class GuthabenException extends Exception {
private final double fehlbetrag;
GuthabenException(String nachricht, double fehlbetrag) {
super(nachricht);
this.fehlbetrag = fehlbetrag;
}
double getFehlbetrag() { return fehlbetrag; }
}
static double abheben(double stand, double betrag) throws GuthabenException {
if (betrag > stand) throw new GuthabenException("Zu wenig Guthaben", betrag - stand);
return stand - betrag;
}
public static void main(String[] args) {
try {
System.out.println(abheben(100, 30));
System.out.println(abheben(100, 130));
} catch (GuthabenException e) {
System.out.println(e.getMessage() + ", es fehlen " + e.getFehlbetrag());
}
}
}70.0 Zu wenig Guthaben, es fehlen 30.0
Ursache mitgeben (Exception Chaining)
public class Verkettet {
static void lade() {
try {
Integer.parseInt("kaputt");
} catch (NumberFormatException e) {
throw new IllegalStateException("Konfiguration ungültig", e); // Ursache bleibt erhalten
}
}
public static void main(String[] args) {
try { lade(); }
catch (IllegalStateException e) {
System.out.println(e.getMessage() + " <- " + e.getCause().getClass().getSimpleName());
}
}
}Konfiguration ung?ltig <- NumberFormatException
Der Stacktrace
Nicht gefangene Ausnahmen beenden das Programm und zeigen den Stacktrace: die Kette der Methodenaufrufe bis zur Fehlerstelle.
public class Absturz {
static int teile(int a, int b) { return a / b; }
public static void main(String[] args) {
System.out.println(teile(1, 0));
}
}Exception in thread "main" java.lang.ArithmeticException: / by zero at Absturz.teile(Absturz.java:2) at Absturz.main(Absturz.java:4)
Lies ihn von oben: Die erste Zeile nennt den Fehler, die nächste die Stelle, wo er auftrat.
Gute Praxis
- Nur fangen, was du behandeln kannst. Ein leeres
catchist fast immer ein Fehler. - Fange nicht
ExceptionoderThrowablepauschal. - Prüfe Eingaben vorher (
IllegalArgumentException), statt Fehler abzufangen. - Gib Ausnahmen aussagekräftige Meldungen und die Ursache mit.
- Nutze
try-with-resourcesfür alles, was geschlossen werden muss. - Logge Fehler (mit Stacktrace), statt
e.printStackTrace()in Produktionscode.
Merke
try { } catch (Typ e) { } finally { };throw new ...löst aus,throwsmeldet an- Checked Exceptions musst du behandeln,
RuntimeExceptions nicht - Spezifische
catch-Blöcke vor allgemeinen try-with-resourcesschließt Ressourcen automatisch- Eigene Ausnahmen erweitern
ExceptionoderRuntimeException; Ursache mitgeben
Aufgabe
Schreibe eine Methode leseZahl(String), die bei ungültiger Eingabe eine eigene Ausnahme wirft, und fange sie in einer Schleife über mehrere Eingaben ab.