Alle Tabs der Lerneinheit (Erklärung · Interaktiv · Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
Erklärung
Eine Exception ist ein außergewöhnliches Ereignis, das den normalen Programmfluss unterbricht. Sie kann durch Programmierfehler, externe Probleme (Datei nicht gefunden, Netzwerk, Timeout, ungültige Nutzereingabe) oder bewusstes throw/raise entstehen, nicht jede Exception ist also zwingend ein Bug. Mit try-catch fängst du sie ab und reagierst kontrolliert. Pflichtthema in Java-Klausuren, in Python ähnlich aber lockerer.
Was du in der Klausur können musst:
- try: Block, in dem eine Exception auftreten könnte
- catch (ExceptionTyp e): fängt den passenden Exception-Typ
- finally: läuft praktisch immer am Ende des try-Blocks (außer bei Sonderfällen wie
System.exit()oder JVM-Crash) - throw: wirft selbst eine Exception aus
- Checked vs Unchecked (Java): Checked müssen mit try-catch oder throws deklariert werden, Unchecked (RuntimeException) nicht
In Klausuren oft gefragt: welche Ausgabe produziert dieser Code? wenn eine Exception in try-catch-finally fließt. Wichtig: finally läuft auch wenn try ein return macht, das ist die häufigste Falle. Plus die Klassiker wie NullPointerException (Aufruf auf null), ArrayIndexOutOfBoundsException (Index außerhalb), NumberFormatException (ungültiger String beim Parsen) und ArithmeticException (z. B. Division durch 0).
Das Problem
Du schreibst:
int a = 10, b = 0;
int result = a / b; // ⚠ ArithmeticException: / by zero
System.out.println(result);
Was passiert? Wenn die Exception ungefangen bleibt, endet zuerst der betroffene Thread und ein Stack-Trace wird ausgegeben. Die JVM beendet sich erst dann, wenn kein Non-Daemon-Thread mehr läuft. In einem typischen Programm mit nur dem Main-Thread heißt das: ungefangene Exception → Programm bricht ab. Sind weitere Non-Daemon-Threads aktiv, läuft das Programm weiter.
Eine Exception ist ein außergewöhnliches Ereignis, das den normalen Programmfluss unterbricht.
Mit Exception Handling kannst du das abfangen und reagieren statt abzustürzen.
Die drei Wörter: try, catch, finally
try {
// Code, der eine Exception werfen könnte
int result = a / b;
System.out.println(result);
} catch (ArithmeticException e) {
// Diese Exception abfangen und behandeln
System.out.println("Division durch 0!");
} finally {
// Wird bei normalem Ablauf praktisch immer ausgeführt
System.out.println("Aufräumen");
}
Drei Möglichkeiten wie der Code laufen kann:
- Kein Fehler, try läuft komplett durch → catch übersprungen → finally
- Erwartete Exception, try bricht ab → catch matcht → finally
- Unerwartete Exception, try bricht ab → kein catch matcht → finally läuft trotzdem → Exception wird weitergegeben nach außen
finally läuft praktisch immer, auch wenn die Exception nicht abgefangen wird, auch wenn ein
returnim try steht. Das ist der Sinn: Aufräumen sicherstellen. Ausnahmen:System.exit(), JVM-Crash oder Prozess-Abbruch von außen.
Python-Variante
Bis hier ging es um Java. Jetzt übertragen wir die Idee auf Python und markieren die Unterschiede: except statt catch, optionales else und kein checked/unchecked.
try:
result = a / b
print(result)
except ZeroDivisionError as e:
print("Division durch 0!")
except Exception as e:
print(f"Anderer Fehler: {e}")
finally:
print("Aufräumen")
Die Grundidee ist ähnlich, aber Python nutzt except statt catch, kennt ein optionales else (läuft wenn KEIN Fehler kam, aber nach try) und hat keine checked Exceptions (keine Compile-Time-Pflicht).
Die Exception-Hierarchie (Java)
In Java sind alle Exceptions Klassen, die voneinander erben:
Throwable
/ \
Error Exception
(nicht |
catchen!) |
┌─────────┴────────┐
IOException RuntimeException
SQLException / | \
... NullPointer Arithmetic ArrayIndex
Exception Exception OutOfBounds
Throwable ist die Wurzel. Du kannst alles "throwbare" mit
catch (Throwable e)abfangen, solltest du aber nie.
Checked vs. Unchecked
In Java zentral wichtig:
| Typ | Beispiele | Pflicht? |
|---|---|---|
| Checked (unter Exception, NICHT RuntimeException) | IOException, SQLException | MUSS behandelt werden (catch oder throws-Deklaration) |
| Unchecked (RuntimeException + Subklassen) | NullPointer, Arithmetic, ArrayIndex | darf behandelt werden, aber muss nicht |
| Error | OutOfMemoryError, StackOverflowError | NICHT abfangen, JVM-Probleme |
Checked = Compiler zwingt dich. Wenn du eine Methode aufrufst, die eine IOException werfen kann, MUSST du sie entweder mit try/catch fangen oder mit
throwsweiterreichen.
// Checked Exception, Compiler verlangt Behandlung
public void readFile() throws IOException {
Files.readString(path); // wirft IOException
}
// Aufrufer muss reagieren:
try {
readFile();
} catch (IOException e) {
e.printStackTrace();
}
Python kennt keine Checked Exceptions: es gibt keine Compile-Time-Pflicht, bestimmte Exceptions zu fangen oder zu deklarieren. Du kannst, musst aber nicht.
Mehrere catch-Blöcke
Du kannst mehrere catch-Blöcke hintereinander setzen, Java probiert sie von oben nach unten durch und nimmt den ersten passenden:
try {
// ...
} catch (ArithmeticException e) { // spezifisch
System.out.println("Math-Fehler");
} catch (NullPointerException e) { // spezifisch
System.out.println("Null-Fehler");
} catch (Exception e) { // Fallback
System.out.println("Anderer Fehler");
}
Die Reihenfolge ist wichtig: spezifische Exceptions zuerst, generelle zuletzt.
catch (Exception e)vor spezifischeren catch-Blöcken macht diese späteren Blöcke unerreichbar und führt zum Compile-Fehler ("Exception already caught").
Multi-Catch (Java 7+)
Wenn die Behandlung gleich ist, mehrere Typen kombinierbar:
try {
// ...
} catch (IOException | SQLException e) {
System.out.println("I/O oder DB-Fehler: " + e.getMessage());
}
Eigene Exception werfen: throw / raise
Manchmal willst du selbst eine Exception auslösen, z.B. bei ungültigen Argumenten:
public void setAlter(int alter) {
if (alter < 0) {
throw new IllegalArgumentException("Alter darf nicht negativ sein: " + alter);
}
this.alter = alter;
}
def set_alter(self, alter):
if alter < 0:
raise ValueError(f"Alter darf nicht negativ sein: {alter}")
self.alter = alter
Eigene Exception-Klasse
Du kannst auch eigene Exception-Typen definieren:
public class UngültigeNoteException extends Exception {
public UngültigeNoteException(double note) {
super("Note " + note + " ist außerhalb 1.0–6.0");
}
}
// Nutzung
public void setNote(double note) throws UngültigeNoteException {
if (note < 1.0 || note > 6.0) {
throw new UngültigeNoteException(note);
}
}
Eigene Exceptions sind sinnvoll wenn: domänenspezifische Fehler, klare Semantik, oft mit zusätzlichen Daten.
try-with-resources / context managers
Klausur-Klassiker. Bei Ressourcen (Datei, DB-Verbindung, Stream) MUSS man hinterher schließen, auch im Fehlerfall.
Alte Methode (Java vor 7):
FileReader fr = null;
try {
fr = new FileReader("data.txt");
// arbeit damit...
} finally {
if (fr != null) fr.close(); // muss in finally!
}
Neu (try-with-resources):
try (FileReader fr = new FileReader("data.txt")) {
// arbeit damit...
}
// fr.close() wird AUTOMATISCH aufgerufen, auch bei Exception
// Falls beim close() selbst eine Exception fliegt, wird sie als
// suppressed Exception angehängt (e.getSuppressed()), die primäre
// Exception aus dem try-Block bleibt die Haupt-Exception.
Voraussetzung: die Ressource muss
AutoCloseableimplementieren. Viele Standard-Klassen tun das (Reader, Writer, Stream, Connection).
In Python macht das with:
with open("data.txt") as f:
# arbeit damit...
# f.close() wird AUTOMATISCH aufgerufen
Best Practices
❌ Nicht alles fangen
try {
// 200 Zeilen Code
} catch (Exception e) {
// alles still abfangen
}
So versteckst du Bugs. Du weißt nie was schief lief.
❌ Exception schlucken
try {
// ...
} catch (IOException e) {
// do nothing ← BAD
}
Mindestens loggen. Sonst verschwindet der Fehler spurlos.
✅ Spezifisch fangen
try {
Files.write(path, data);
} catch (FileNotFoundException e) {
// Datei nicht da → Default benutzen
} catch (IOException e) {
// anderes I/O-Problem → höher melden
throw new RuntimeException("I/O-Fehler", e);
}
✅ try-Blöcke klein und logisch zusammenhängend halten
Nicht unnötig 100 Zeilen einpacken. Aber: oft gehören mehrere zusammenhängende Operationen in denselben try, z. B. Ressource öffnen, lesen, parsen, schließen.
✅ Exceptions nicht für normalen Kontrollfluss missbrauchen
Exceptions sollten nicht als if-Ersatz oder Schleifensteuerung verwendet werden. Aber: bei Parsing von Nutzereingaben ist try { Integer.parseInt(s); } catch (NumberFormatException e) { ... } oft die idiomatische Lösung, vorab-Regex kann doppelte Logik erzeugen.
// Pragmatisch: Parsing mit catch ist okay
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return 0; // Default bei ungültiger Eingabe
}
// Anti-Pattern: Exception als Schleifensteuerung
try {
while (true) list.get(i++); // ← so NICHT
} catch (IndexOutOfBoundsException e) { /* Schleifenende */ }
Exceptions sind teurer (Stack-Trace-Aufbau kostet Zeit) als normale if-Verzweigungen, für normale Schleifen- oder Programmsteuerung also ungeeignet.
Klausur-Tricks
Trick 1, finally läuft bei normaler JVM-Ausführung praktisch immer: auch wenn Exception nicht abgefangen wird. Auch wenn return im try steht. Auch wenn in catch eine neue Exception fliegt. (Ausnahmen: System.exit(), JVM-Crash, externer Prozessabbruch.)
Trick 2, Reihenfolge der catch-Blöcke: spezifisch → generell. catch (Exception e) vor spezifischeren catch-Blöcken macht diese späteren Blöcke unerreichbar und führt zum Compile-Fehler. Ein einzelner catch (Exception e) oder als letzter Fallback ist hingegen gültig.
Trick 3, Checked vs. Unchecked:
- IOException → checked → muss behandelt
- NullPointerException → unchecked → muss nicht
- Wenn die Klausur fragt "muss der Compiler zustimmen?" → checken ob die Exception unter RuntimeException liegt
Trick 4, try ohne catch geht in Java NICHT (Ausnahme: try-with-resources). Normales try { ... } allein ist Compile-Fehler, du brauchst catch oder finally (oder beides). Die Ausnahme: try (Resource r = ...) { ... } ist auch ohne catch/finally gültig, weil das Schließen der Ressource automatisch geregelt wird.
Trick 5, Exception fortpflanzen: wenn du sie nicht fangen willst, mit throws deklarieren:
public void method() throws IOException { // Aufrufer muss kümmern
Files.readString(path);
}
Trick 6, Cause behalten: wenn du in catch eine neue Exception wirfst, wickle die alte ein:
catch (IOException e) {
throw new RuntimeException("I/O-Problem beim Laden", e); // 'e' als cause
}
Sonst geht der ursprüngliche Stack-Trace verloren.
Trick 7, Exception in finally: wenn finally selbst eine Exception wirft, überschreibt sie eine eventuelle Exception aus dem try-Block. Verhindern: Logik in finally robust halten.
Trick 8, try-with-resources Reihenfolge: ressourcen werden in umgekehrter Reihenfolge geschlossen.
try (A a = ...; B b = ...) {
// schließt b, dann a
}
Trick 9, Exception oder Error?:
- Exception: Anwendungs-Probleme, kann man oft sinnvoll behandeln
- Error: JVM-Probleme (OutOfMemory, StackOverflow), nicht abfangen
Wo brauchst du Exception Handling?
- I/O: jeder Datei-, Netzwerk- und DB-Zugriff
- User Input: Konvertierung von Strings (parseInt, parseDouble)
- API-Aufrufe: HTTP-Fehler, Timeouts, JSON-Parse-Fehler
- Datenbank-Operationen: Constraint-Violation, Lock-Timeout
- Validierung: Domain-Logik (z.B. Note 7.0 → eigene Exception)
- Ressourcen-Management: Verbindungen schließen, Files schließen
- Multi-Threading: InterruptedException
Faustregel: an jeder System-Grenze (Datei, Netzwerk, DB) gehört Exception-Handling. Innerhalb deiner eigenen Logik: nur wenn nötig, lieber vorher prüfen.
Interaktiv
Try-Catch-Simulator
Wähle eine der vier vordefinierten Beispiel-Szenarien (Division durch 0, NullPointer, Ohne Fehler, Falscher catch-Typ) im Simulator und sieh Schritt-für-Schritt, wie der Kontrollfluss läuft:
- Aktive Zeile wird hervorgehoben
- Bei einer Exception leuchtet die Zeile in Vermillion
- Der passende catch-Block wird gefunden, andere übersprungen
- finally läuft bei normaler JVM-Ausführung am Ende, auch ohne Exception
- Live-Stdout rechts zeigt was ausgegeben wird
Probier folgendes:
- Division durch 0: ArithmeticException wird abgefangen, finally läuft
- NullPointerException: gleiches Muster mit anderer Exception
- Ohne Fehler: catch wird übersprungen, finally läuft trotzdem
- Falscher catch-Typ: spezifischer catch matcht nicht, generischer catch(Exception) fängt am Ende
Klicke auf Step für manuelles Durchlaufen oder auf Play für automatische Animation.
Interaktive Visualisierung
Simuliert Exception-Handling mit try, catch, finally und Exception-Hierarchie.
Faustregel zum Mitnehmen: try → catch (matchender Typ) → finally (bei normaler JVM-Ausführung praktisch immer). Die Reihenfolge der catch-Blöcke matcht von oben nach unten, spezifischer Typ zuerst, allgemeiner Fallback zuletzt.
Code-Stepper: Klausur-Klassiker durchspielen
Drei interaktive Demos zu den häufigsten Exception-Handling-Klausurfallen: finally läuft trotz uncaught Exception, return-Konflikt zwischen try und finally (Trick 7), und Close-Reihenfolge bei try-with-resources (Trick 8). Klicke auf Step oder Auto und beobachte, wie der Kontrollfluss durch die Blöcke wandert.
1. finally läuft TROTZ ungefangener Exception
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
2. return im try + return im finally (Quiz-Frage 8 live)
Klassische Klausur-Falle: was passiert, wenn beide Blöcke ein return haben? Live-Trace zeigt: finally läuft NACH dem return im try und überschreibt den Rückgabewert. Best Practice: niemals return in finally.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
3. try-with-resources: Close-Reihenfolge ist umgekehrt
Trick 8: bei mehreren Ressourcen werden sie in umgekehrter Reihenfolge geschlossen (LIFO). Das ist meist genau richtig, weil spätere Ressourcen oft von früheren abhängen.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Quiz
Klausurfragen mit Lösungen (8)
- F1.Welche Aussage über finally ist KORREKT?
Antwort: finally läuft bei normaler JVM-Ausführung praktisch immer, egal ob Exception oder nicht
Erklärung: finally läuft bei normaler JVM-Ausführung **praktisch immer**: ohne Exception, mit Exception (egal ob abgefangen oder nicht), nach return im try. Genau dafür ist es da: garantiertes Aufräumen (Datei schließen, Verbindung trennen). Sonderfälle, in denen finally NICHT läuft: `System.exit()`, JVM-Crash, externer Prozess-Abbruch.
- F2.Was ist der Unterschied zwischen Checked und Unchecked Exceptions in Java?
Antwort: Checked Exceptions MÜSSEN behandelt werden (catch oder throws), Unchecked nicht
Erklärung: Checked Exceptions (z.B. IOException) zwingen den Compiler dazu, dass der Aufrufer reagiert (catch oder throws). Unchecked sind RuntimeException-Subklassen (NullPointer, Arithmetic), können behandelt werden, müssen aber nicht. Python kennt diese Unterscheidung nicht.
- F3.Was passiert bei diesem Code?
Antwort: Compile-Fehler, Exception ist Oberklasse, fängt schon alles
Erklärung: catch(Exception) fängt schon alle Subklassen, der zweite catch(ArithmeticException) wäre nie erreichbar. Der Compiler erkennt das und meldet 'Exception ArithmeticException already caught'. Reihenfolge: spezifisch zuerst.
- F4.Was passiert, wenn eine Exception nicht abgefangen wird?
Antwort: Sie wird an den Aufrufer weitergegeben (propagiert), bis irgendwer fängt oder das Programm abbricht
Erklärung: Eine ungefangene Exception propagiert den Aufrufstack hoch. Wenn niemand fängt, endet der betroffene Thread mit Stack-Trace; in typischen Main-Thread-Beispielen bricht dadurch das Programm ab. finally-Blöcke laufen aber trotzdem auf dem Weg nach oben.
- F5.Wozu nutzt man try-with-resources (oder Pythons with)?
Antwort: Um Ressourcen automatisch zu schließen, auch wenn eine Exception fliegt
Erklärung: try-with-resources / with sorgt dafür, dass die Ressource (Datei, Stream, Connection) automatisch geschlossen wird, auch im Fehlerfall. Ersetzt das alte 'finally { resource.close() }'-Boilerplate. In Java muss die Ressource `AutoCloseable` implementieren; in Python muss das Objekt das Context-Manager-Protokoll mit `__enter__` und `__exit__` unterstützen.
- F6.Welcher Fehler ist eine RuntimeException (unchecked) in Java?
Antwort: NullPointerException
Erklärung: NullPointerException erbt von RuntimeException → unchecked. IOException, SQLException und ClassNotFoundException sind **checked Exceptions**, weil sie von Exception erben, aber NICHT von RuntimeException → müssen behandelt werden.
- F7.Was bewirkt throws in einer Methodensignatur?
Antwort: Sagt dem Compiler: diese Methode kann diese Exception werfen, Aufrufer muss sie behandeln
Erklärung: throws deklariert: 'diese Methode könnte diese (checked) Exception weiterreichen'. Der Aufrufer muss dann reagieren (catch oder selbst throws). Bei unchecked Exceptions ist throws optional. throw (ohne s) wirft tatsächlich eine Exception.
- F8.Was wird bei diesem Code zurückgegeben?
Antwort: 2
Erklärung: Hinterhältige Klausurfrage! finally läuft bei normaler JVM-Ausführung praktisch immer, auch nach return im try. Wenn finally selbst return hat, überschreibt es das try-return. Hier: 2. Best Practice: nie return in finally, sehr verwirrend.
try {
int x = a / 0;
} catch (Exception e) {
// ...
} catch (ArithmeticException e) {
// ...
}public static int test() {
try {
return 1;
} finally {
return 2;
}
}