W3docs

Java try...catch

Laufzeitfehler in Java mit try...catch-Blöcken abfangen und das Programm am Laufen halten, wenn etwas schiefläuft.

try/catch ist die kleinste Einheit der Ausnahmebehandlung in Java. Du umschließt den riskanten Code mit try und folgst ihm mit einem oder mehreren catch-Blöcken, die angeben, was bei einer bestimmten Art von Ausnahme zu tun ist. Zusammen bilden sie eine einzige Anweisung; du kannst nicht das eine ohne das andere haben (oder ohne ein finally, das wir in einem späteren Kapitel kennenlernen).

Aufbau

try {
  // code that might throw
} catch (ExceptionType e) {
  // code that runs only if a matching exception was thrown
}

Einige Dinge sind auf den ersten Blick leicht zu übersehen:

  • Die Variable im catch (e per Konvention) ist auf den catch-Block beschränkt. Du kannst sie benennen, wie du möchtest; e und ex sind die gängigen Bezeichnungen.
  • Der catch-Parameter ist effektiv final — du kannst ihn lesen, solltest ihn aber nicht neu zuweisen. Ihn als unveränderlich zu behandeln, macht den Code leichter nachvollziehbar.
  • Der try-Block hat seinen eigenen Gültigkeitsbereich. Variablen, die darin deklariert werden, sind weder für den catch noch nach der Anweisung sichtbar. Wenn du einen im try berechneten Wert später benötigst, deklariere ihn außerhalb.
String content;
try {
  content = Files.readString(path);
} catch (IOException e) {
  content = "";   // works because `content` was declared outside the try
}
System.out.println(content);

Welche catch-Blöcke greifen

Ein catch (T e)-Block wird ausgeführt, wenn die geworfene Ausnahme eine Instanz von T ist — einschließlich aller Untertypen von T. Also:

  • catch (Exception e) fängt fast alles ab: IOException, NullPointerException, eigene benutzerdefinierte Ausnahmen.
  • catch (RuntimeException e) fängt Laufzeitfehler ab, aber keine geprüften Ausnahmen wie IOException.
  • catch (NullPointerException e) fängt nur NPEs ab (und Unterklassen, von denen es typischerweise keine gibt).

Innerhalb des try wird nur der erste passende catch ausgeführt. Wenn du einen breiteren Typ vor einem engeren angibst, ist der engere catch unerreichbar und der Compiler verweigert dies:

try { ... }
catch (Exception e)        { ... }   // matches everything
catch (IOException e)      { ... }   // ERROR: unreachable

Liste catch-Blöcke immer vom Spezifischsten zum Allgemeinsten.

Was man in einem catch tun sollte

Ein catch-Block ist kein Ort, um Ausnahmen verschwinden zu lassen. Es ist ein Ort, um zu entscheiden, was bei einem bekannten Fehler zu tun ist. Die realistischen Optionen sind:

  • Wiederherstellen — erneut versuchen, auf einen Standardwert zurückgreifen, auf eine andere Ressource wechseln. Das ist der beste Fall, und er ist seltener als man denkt.
  • Protokollieren und erneut werfen — Details aufzeichnen und die Ausnahme an einen übergeordneten Handler weiterpropagieren, der weiß, was zu tun ist.
  • Umhüllen und erneut werfen — eine low-level-Ausnahme in eine übersetzen, die zum Vokabular dieser Ebene passt (IOExceptionConfigLoadException).
  • In einen Rückgabewert übersetzen — wenn der Fehler wirklich erwartet wird (z. B. beim Parsen von Benutzereingaben), Optional.empty() oder einen Sentinel-Wert zurückgeben.

Was man nicht tun sollte: eine Ausnahme abfangen und stillschweigend weitermachen, ohne sie zu protokollieren oder darauf zu reagieren. So verschwinden Fehler spurlos. Wir kommen darauf im Kapitel über Best Practices zurück.

Ausnahmeinformationen lesen

Der catch-Parameter ist ein Objekt. Drei Methoden, die du ständig verwenden wirst:

  • e.getMessage() — die menschenlesbare Beschreibung. Kann null sein.
  • e.toString() — Klassenname plus Nachricht, z. B. java.io.IOException: file not found.
  • e.printStackTrace() — schreibt den Stack-Trace nach System.err. Praktisch beim Debuggen, aber verwende einen richtigen Logger in Produktionscode (er leitet den Trace durch denselben Kanal wie alles andere).

Eine vierte Methode, e.getCause(), gibt die zugrunde liegende Ausnahme zurück, wenn eine eingehüllt wurde — nützlich, wenn du den ursprünglichen Fehler innerhalb einer Übersetzungsschicht verstehen musst.

Exception abfangen vs. Throwable abfangen

Du kannst catch (Throwable t) schreiben und es fängt alles ab — einschließlich Errors wie OutOfMemoryError. Tu das nicht. Errors bedeuten, dass die JVM in Schwierigkeiten steckt und dein Code nicht in der Lage ist, sinnvoll darauf zu reagieren. Das Abfangen von Throwable verdeckt Fehler, die den Prozess zum Absturz bringen sollten.

Bleibe bei Exception oder einem ihrer Untertypen. Wenn du das Unerwartete wirklich protokollieren musst, bevor es stirbt, ist die richtige Form catch (RuntimeException e) { log; throw; }.

Ein ausgearbeitetes Beispiel

Ein kleines Hilfsprogramm, das eine Ganzzahl aus jeder Eingabezeile parst. Einige Zeilen sind gültige Zahlen, andere nicht, eine ist null. Wir verwenden try/catch, um über die fehlerhaften Zeilen hinwegzukommen und zu zählen, was passiert ist. Der Aufruf von line.trim() wirft eine NullPointerException beim null-Eintrag, während Integer.parseInt eine NumberFormatException wirft bei allem, was keine Ganzzahl ist — zwei verschiedene Ausnahmetypen, jeder mit seinem eigenen catch.

java— editable, runs on the server

Die Ausgabe lautet:

not an integer: "hello"
null line — skipped
not an integer: "3.14"
---
parsed: 3, failed: 3, sum: 118

Zwei Dinge sind zu beachten. Erstens beendet sich das Programm ordnungsgemäß. Ohne die catch-Blöcke würde die erste fehlerhafte Zeile den Durchlauf beenden und nichts danach würde gezählt. Zweitens werden die zwei Fehler unterschieden: Die null-Zeile nimmt den NullPointerException-Zweig und gibt ihre eigene Nachricht aus, während "hello" und "3.14" den NumberFormatException-Zweig nehmen. Die Auswahl von catch-Blöcken nach Typ ist der Weg, verschiedene Fehler nicht durcheinander geraten zu lassen.

Was kommt als Nächstes

Ein catch ist der einfachste Fall. Echter Code muss normalerweise mehrere verschiedene Fehler gleichzeitig behandeln. Weiter zu Java mehrere catch-Blöcke und Multi-catch. Danach runden der finally-Block und try-with-resources die Syntax ab, während throw und throws die andere Seite abdecken: Fehler aus eigenem Code signalisieren.

Übungen

Übung
Ein try-Block führt `a[5] = 1;` auf einem 2-elementigen Array aus, mit `catch (RuntimeException e)` zuerst und `catch (ArrayIndexOutOfBoundsException e)` als zweitem. Was passiert?
Ein try-Block führt `a[5] = 1;` auf einem 2-elementigen Array aus, mit `catch (RuntimeException e)` zuerst und `catch (ArrayIndexOutOfBoundsException e)` als zweitem. Was passiert?
Was this page helpful?