Was eine Exception ist
Eine Exception ist Javas Art zu sagen: „Etwas ist schiefgelaufen und ich kann nicht normal weitermachen." Statt einen falschen Wert zurückzugeben oder deine Daten stillschweigend zu beschädigen, erzeugt die Laufzeitumgebung ein Exception-Objekt, das das Problem beschreibt, und wirft es. Die normale Ausführung stoppt an dieser Stelle, und Java beginnt, nach Code zu suchen, der weiß, wie man mit der Situation umgeht.
Behandelt sie niemand, erreicht die Exception die Spitze deines Programms, die JVM gibt einen Stack-Trace aus, und der Prozess beendet sich mit einem Status ungleich null.
Beachte, dass "after" nie ausgegeben wird. In dem Moment, in dem numbers[5] ausgewertet wird, wird eine ArrayIndexOutOfBoundsException geworfen und der Rest von main wird verworfen.
Einen Stack-Trace lesen
Wenn eine Exception unbehandelt bleibt, erhältst du eine Ausgabe wie diese. Sie wirkt einschüchternd, ist aber nur eine Liste:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.divide(Main.java:8)
at Main.main(Main.java:4)
Lies sie von oben nach unten:
- Die erste Zeile ist der Typ (
ArithmeticException) und eine Meldung (/ by zero). - Jede
at ...-Zeile ist ein Stack-Frame. Die oberste ist die Stelle, an der die Exception tatsächlich geworfen wurde -dividein Zeile 8. Darunter steht der Aufrufer,mainin Zeile 4.
Die oberste at-Zeile ist die Stelle, an der du zuerst nachschaust. Die Frames darunter beantworten die Frage „Wie sind wir hierher gekommen?".
Führe dies aus, und der Trace zeigt direkt auf die Zeile a / b und dann main als Aufrufer. Diese Aufrufkette ist das nützlichste Debugging-Werkzeug, das Java dir gratis schenkt.
Die Exception-Hierarchie
Jede Exception ist ein Objekt, und alle stammen von Throwable ab. Die beiden Zweige, die zählen:
Error- schwerwiegende Probleme, die die JVM auslöst, wieOutOfMemoryErroroderStackOverflowError. Diese fängst du in der Regel nicht.Exception- Probleme, die dein Programm vernünftigerweise vorhersehen und behandeln kann. Darin sitztRuntimeException, der Elternteil der alltäglichen Bugs wieNullPointerException.
Throwable
├── Error (don't catch: OutOfMemoryError, StackOverflowError)
└── Exception
├── IOException (checked)
├── SQLException (checked)
└── RuntimeException (unchecked)
├── NullPointerException
├── ArithmeticException
└── ArrayIndexOutOfBoundsException
Weil dies echte Klassen sind, kann eine Exception eine Meldung tragen, und du kannst sie über sich selbst befragen:
getMessage() liefert den Text nach dem Doppelpunkt im Stack-Trace; getClass().getSimpleName() gibt dir den Exception-Typ als Namen.
Checked vs. unchecked
Das ist die Unterscheidung, die Neulinge am meisten verwirrt.
- Unchecked Exceptions erweitern
RuntimeException. Sie bedeuten meist einen Bug in deinem Code - ein unerwartetesnull, ein falscher Index, eine Division durch null. Der Compiler zwingt dich nicht, sie zu behandeln. - Checked Exceptions erweitern
Exception, aber nichtRuntimeException(zum BeispielIOException). Sie stehen für Bedingungen außerhalb deiner Kontrolle - eine fehlende Datei, eine abgebrochene Netzwerkverbindung. Der Compiler zwingt dich, sie entweder zu fangen oder zu deklarieren.
Wenn eine Methode eine checked Exception werfen kann, muss sie das mit throws angeben, und jeder Aufrufer muss damit umgehen:
Entferne throws Exception aus main, und der Code lässt sich nicht einmal kompilieren - das ist der Compiler, der den Vertrag der checked Exceptions durchsetzt. Unchecked Exceptions verlangen das nie.
Eigene Exceptions werfen
Du reagierst nicht nur auf Exceptions - du kannst sie auch auslösen. Verwende throw mit einem neuen Exception-Objekt, um zu signalisieren, dass ein Argument oder ein Zustand ungültig ist. Das ist weitaus besser, als einen magischen Wert wie -1 zurückzugeben und zu hoffen, dass der Aufrufer ihn prüft.
Füge immer eine Meldung hinzu, die erklärt, was falsch war, und idealerweise den problematischen Wert - dein zukünftiges Ich, das den Stack-Trace liest, wird es dir danken. IllegalArgumentException und IllegalStateException sind die beiden, zu denen du beim Validieren von Eingaben am häufigsten greifst.
Häufige Stolpersteine
- Exceptions verschlucken. Eine Exception zu fangen und nichts zu tun (ein leerer Block) verbirgt Bugs. Logge sie zumindest; meist solltest du sie behandeln oder erneut werfen.
Exceptionzu breit fangen. Die BasisklasseException(oder schlimmer,Throwable) zu fangen, kann Probleme verbergen, die du nicht behandeln wolltest. Fange den konkreten Typ, den du erwartest.- Den Trace von unten nach oben lesen. Der relevanteste Frame ist die oberste
at-Zeile, nicht die unterste. Beginne dort. ErrormitExceptionverwechseln. Versuche nicht, dich von einemOutOfMemoryErroroderStackOverflowErrorzu erholen; behebe stattdessen die zugrunde liegende Ursache.
Weiter: try-catch
Jetzt weißt du, was Exceptions sind und wie man sie liest. Die nächste Seite behandelt, wie man sie tatsächlich behandelt: riskanten Code in einen try-Block einpacken, im catch reagieren und Aufräumcode im finally ausführen, damit dein Programm weiterläuft, statt abzustürzen.
Häufig gestellte Fragen
Was ist eine Exception in Java?
Eine Exception ist ein Objekt, das ein Problem darstellt, das während der Laufzeit eines Programms erkannt wird - etwa eine Division durch null, ein Zugriff über das Ende eines Arrays hinaus oder der Aufruf einer Methode auf null. Tritt das Problem auf, wirft Java die Exception: Der normale Ablauf wird gestoppt und es wird Code gesucht, der sie behandeln kann. Behandelt sie niemand, gibt das Programm einen Stack-Trace aus und beendet sich.
Was ist der Unterschied zwischen einer checked und einer unchecked Exception in Java?
Checked Exceptions (Unterklassen von Exception, aber nicht von RuntimeException, z. B. IOException) müssen entweder gefangen oder mit throws deklariert werden - der Compiler erzwingt das. Unchecked Exceptions (Unterklassen von RuntimeException, z. B. NullPointerException, ArrayIndexOutOfBoundsException) signalisieren meist Programmierfehler und benötigen keine Deklaration. Error (wie OutOfMemoryError) ist ebenfalls unchecked und in der Regel nicht zum Fangen gedacht.
Wie lese ich einen Java-Stack-Trace?
Lies ihn von oben nach unten. Die erste Zeile nennt den Exception-Typ und die Meldung (z. B. java.lang.ArithmeticException: / by zero). Jede at ...-Zeile darunter ist ein Stack-Frame, der Methode, Datei und Zeilennummer zeigt - beginnend dort, wo die Exception geworfen wurde, und rückwärts durch die Aufrufer. Die oberste at-Zeile ist fast immer die Stelle, an der du zuerst nachschaust.