1. Einführung in den Block finally
Wenn Sie mit Ressourcen arbeiten – Dateien, Netzwerkverbindungen, Datenbanken – ist es wichtig, sicherzustellen, dass sie geschlossen oder immer freigegeben werden, selbst wenn während der Arbeit ein Fehler auftritt. In Java gibt es dafür den speziellen Block finally.
Wie funktioniert finally?
Der Block finally ist Teil der Konstruktion try-catch-finally. Der Code in finally wird immer ausgeführt (wenn er geschrieben wurde) – unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht. Selbst wenn es im try ein return gab oder eine Ausnahme geworfen wurde – finally wird trotzdem ausgeführt (es sei denn, der Computer wird ausgeschaltet oder das Programm wird zwangsweise über System.exit(0) beendet).
Syntax:
try {
// Code, in dem eine Ausnahme auftreten kann
} catch (ExceptionType e) {
// Fehlerbehandlung
} finally {
// Dieser Code wird immer ausgeführt!
}
Beispiel
try {
System.out.println("Start");
int result = 10 / 0; // hier tritt ein Fehler auf
System.out.println("Ergebnis: " + result);
} catch (ArithmeticException e) {
System.out.println("Fehler: Division durch Null");
} finally {
System.out.println("Dieser Code wird auf jeden Fall ausgeführt");
}
Ausgabe:
Start
Fehler: Division durch Null
Dieser Code wird auf jeden Fall ausgeführt
Was passiert?
- Im try-Block versuchen wir, zwei Zahlen zu teilen, und erhalten einen Fehler.
- Wenn beim Teilen ein Fehler auftritt, fängt catch ihn ab.
- Aber! In jedem Fall wird finally ausgeführt und schreibt eine Meldung in die Konsole.
2. finally ohne catch
Es gibt drei Varianten der Konstruktion:
- Vollständig: try-catch-finally
- Ohne finally: try-catch
- Ohne catch: try-finally
Die dritte Variante wird verwendet, wenn die Ausnahme von einer Methode eine Ebene höher abgefangen und verarbeitet werden soll. Der Block finally ist jedoch nötig, um die Ausführung eines bestimmten Codes zu garantieren:
- Schließen von Dateien, Netzwerkverbindungen, Datenbanken.
- Freigeben beliebiger Ressourcen (z. B. Sperren/Locks).
- Logging: Informationen über den Abschluss einer Operation protokollieren.
Beispiel:
try {
System.out.println("Wir teilen Zahlen");
int result = 10 / 0; // Fehler!
System.out.println("Ergebnis: " + result);
} finally {
System.out.println("Der finally-Block wurde ausgeführt");
}
Ausgabe:
Wir teilen Zahlen
Der finally-Block wurde ausgeführt
Exception in thread "main" java.lang.ArithmeticException: / by zero
Wann wird finally NICHT ausgeführt?
Es wird fast immer ausgeführt. Ausnahmen – wenn:
- Das Programm zwangsweise mit System.exit(0) beendet wurde.
- Der Thread, in dem finally ausgeführt wird, gewaltsam beendet wurde.
- Der Computer ausgeschaltet wird.
3. Der Operator throw: eine Ausnahme selbst auslösen
Manchmal „wirft“ Java Ausnahmen selbst (z. B. Division durch Null, Arraygrenzen überschritten). Es gibt jedoch Situationen, in denen Sie ausdrücklich signalisieren wollen: „Das ist ein Fehler! Ich kann die Ausführung nicht fortsetzen!“. Dafür gibt es in Java den Operator throw.
Analogie: Wenn Sie im Laden abgelaufene Ware sehen – legen Sie Beschwerde ein. Genauso im Code: Wenn etwas nicht stimmt – werfen Sie eine Ausnahme.
Syntax von throw
throw new ExceptionType("Fehlermeldung");
ExceptionType ist eine beliebige Klasse, die von Throwable erbt (gewöhnlich Exception oder RuntimeException). In den Klammern steht eine Nachricht, die hilft zu verstehen, was schiefgelaufen ist.
Beispiel: Überprüfung der Methodenargumente
public static int safeDivide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Der Divisor darf nicht null sein");
}
return a / b;
}
Verwendung:
public static void main(String[] args) {
try {
int result = safeDivide(10, 0);
System.out.println("Ergebnis: " + result);
} catch (IllegalArgumentException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
Ausgabe:
Fehler: Der Divisor darf nicht null sein
Wann sollte throw verwendet werden?
- Überprüfung von Methodenargumenten (z. B. wenn null übergeben wird oder Daten ungültig sind).
- Überprüfung des Objektzustands (z. B. der Versuch, Geld von einem Konto abzuheben, auf dem 0 € liegt).
- Innerhalb von catch – wenn Sie die Ausnahme „weiterwerfen“ wollen (z. B. um zusätzliche Informationen hinzuzufügen).
4. Kombination von try-catch-finally und throw
Manchmal arbeiten diese Konstruktionen zusammen. Beispielsweise fangen Sie eine Ausnahme ab, entscheiden danach aber, eine eigene, informativerere zu werfen.
public static int parseAndDivide(String text, int divisor) {
try {
int number = Integer.parseInt(text);
if (divisor == 0) {
throw new IllegalArgumentException("Der Divisor darf nicht null sein");
}
return number / divisor;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Die Zeichenfolge '" + text + "' ist keine Zahl");
} finally {
System.out.println("Versuch, die Zeichenfolge zu verarbeiten: " + text);
}
}
Verwendung:
try {
int result = parseAndDivide("42a", 2);
System.out.println("Ergebnis: " + result);
} catch (IllegalArgumentException e) {
System.out.println("Fehler: " + e.getMessage());
}
Ausgabe:
Versuch, die Zeichenfolge zu verarbeiten: 42a
Fehler: Die Zeichenfolge '42a' ist keine Zahl
Wichtiger Punkt: return und finally
Selbst wenn es im try-Block ein return gibt, wird finally trotzdem ausgeführt!
public static int getValue() {
try {
return 10;
} finally {
System.out.println("finally wird trotzdem ausgeführt!");
}
}
Der Aufruf von getValue() gibt aus:
finally wird trotzdem ausgeführt!
5. Typische Fehler beim Einsatz von finally und throw
Fehler Nr. 1: Ressource ohne finally nicht geschlossen.
Ein sehr häufiges Problem: Datei geöffnet, nicht geschlossen – Ressourcenleck. Verwenden Sie immer finally (oder try-with-resources, darüber sprechen wir später).
Fehler Nr. 2: Ausnahme ausgelöst, aber nicht behandelt.
Wenn Sie eine Ausnahme mit throw auslösen, sie aber nirgends abfangen (kein try-catch), endet das Programm mit einem Fehler. Denken Sie immer daran, wer Ihre Ausnahme abfängt.
Fehler Nr. 3: return in finally.
Wenn Sie versehentlich ein return innerhalb von finally schreiben, „überstimmt“ es alle vorherigen return oder throw. Das kann zu sehr schwer auffindbaren Bugs führen. Davon ist dringend abzuraten!
public int tricky() {
try {
return 1;
} finally {
return 2; // GEFÄHRLICH: Es wird 2 zurückgegeben, nicht 1!
}
}
Ergebnis: Es wird 2 zurückgegeben, obwohl im try 1 stand.
Fehler Nr. 4: Verlust von Ausnahmeinformationen.
Wenn Sie eine Ausnahme abfangen und anschließend eine neue werfen, ohne die alte (e) mitzunehmen, verlieren Sie den Stacktrace, was das Debuggen erschwert. Besser so schreiben:
catch (NumberFormatException e) {
throw new IllegalArgumentException("Fehler bei der Konvertierung", e);
}
GO TO FULL VERSION