W3docs

Java try-with-resources

AutoCloseable-Ressourcen in Java mit der try-with-resources-Anweisung automatisch schließen.

Die try-with-resources-Anweisung ist Javas Lösung für das Muster „öffnen, verwenden, immer schließen". Du deklarierst eine Ressource am Anfang des try-Blocks, und die JVM garantiert, dass sie beim Verlassen des Blocks geschlossen wird — egal ob der Block normal zurückgekehrt ist, eine Ausnahme geworfen hat oder durch einen anderen Kontrollfluss verlassen wurde. Das ausführliche finally-Muster reduziert sich auf eine einzige Deklaration.

Dieses Kapitel behandelt die Syntax, was als Ressource gilt, wie mehrere Ressourcen geschlossen werden, den Mechanismus unterdrückter Ausnahmen, der das Feature zuverlässig macht, und wann man nicht darauf zurückgreifen sollte.

Die Form

try (FileReader reader = new FileReader(path)) {
  // use reader
}

Das war's. Kein finally, kein explizites close(). Wenn der try-Block endet, ruft die JVM automatisch reader.close() auf. Alle catch- und finally-Klauseln, die du hinzufügst, funktionieren weiterhin — sie werden nach dem Schließen der Ressource ausgeführt.

try (FileReader reader = new FileReader(path)) {
  // use reader
} catch (IOException e) {
  // handle — at this point the reader is already closed
}

Was eine Ressource sein kann

Eine Ressource muss AutoCloseable implementieren (oder das ältere Subinterface Closeable). Dieses Interface deklariert eine einzige Methode:

public interface AutoCloseable {
  void close() throws Exception;
}

Die meisten Typen, die man verwenden würde, implementieren es bereits: InputStream, OutputStream, Reader, Writer, Connection, Statement, ResultSet, Scanner, Lock-Wrapper und viele Drittanbieter-Clients. Wenn du eine Klasse schreibst, die eine Ressource besitzt, implementierst du AutoCloseable selbst.

Mehrere Ressourcen

Deklarationen werden durch Semikolons getrennt. Ressourcen werden in umgekehrter Reihenfolge der Deklaration geschlossen, also wird die zuletzt geöffnete als erste geschlossen:

try (
  FileInputStream  in  = new FileInputStream(src);
  FileOutputStream out = new FileOutputStream(dst)
) {
  in.transferTo(out);
}
// closes `out` first, then `in`

Die umgekehrte Reihenfolge ist wichtig, weil die zweite Ressource oft von der ersten abhängt. Die abhängige zuerst zu schließen lässt die Grundlage für einen Moment länger intakt, was die sicherere Reihenfolge ist.

Vorhandene Ressourcen wiederverwenden (Java 9+)

Wenn du bereits eine Variable hast, die ein AutoCloseable enthält, musst du keine neue deklarieren — übergib sie namentlich:

BufferedReader reader = openSomewhere();
try (reader) {
  // use it
}

Die Variable muss final oder effektiv final sein (du kannst sie nicht neu zuweisen). Diese Form ist nützlich, wenn eine Ressource anderswo erstellt wurde — auf Kosten einer weniger sichtbaren Schließoperation. Verwende sie, wenn die vorhandene Variable direkt verfügbar ist; andernfalls bevorzuge die inline-Deklaration.

Unterdrückte Ausnahmen

Hier ist der subtile Teil. Was passiert, wenn der try-Body eine Ausnahme wirft und der close()-Aufruf ebenfalls eine Ausnahme wirft? Vor Java 7 würde die Close-Ausnahme aus dem finally-Block die ursprüngliche ersetzen — und der echte Grund ginge verloren.

try-with-resources löst dieses Problem. Die ursprüngliche Ausnahme wird an den Aufrufer geworfen; alle Ausnahmen zur Schließzeit werden als unterdrückte Ausnahmen daran angehängt und können mit Throwable.getSuppressed() abgerufen werden:

try (Resource r = new Resource()) {
  r.use();          // throws A
}                   // close() throws B
// caller sees A
// A.getSuppressed() returns [B]

Das Standard-printStackTrace() gibt auch unterdrückte Ausnahmen aus (Suppressed: ...-Zeilen unter der Hauptausnahme). Du musst das fast nie manuell behandeln — die Sprache erhält die Kette für dich.

Eine typische reale Form

Ein gängiges Muster, das alles zusammenbringt:

public List<String> readAllLines(Path p) throws IOException {
  try (BufferedReader reader = Files.newBufferedReader(p)) {
    return reader.lines().toList();
  }
}

Zu beachten: kein explizites Close, kein finally, der Body kann direkt return ausführen, und der Aufrufer erhält trotzdem eine saubere IOException, wenn etwas schiefläuft.

Wann try-with-resources das falsche Werkzeug ist

Es ist das richtige Werkzeug, wenn du die gesamte Lebensdauer der Ressource für einen Block besitzt. Es ist falsch, wenn:

  • Die Ressource länger als den Block lebt — zum Beispiel eine über Aufrufe hinweg zwischengespeicherte Verbindung. Sie am Ende der Methode zu schließen würde den nächsten Benutzer beeinträchtigen.
  • Du die Ressource nicht besitzt — ein Aufrufer hat sie dir übergeben. Er wird sie selbst schließen.

In diesen Fällen überlasse das Schließen dem Eigentümer. Wenn du nicht weißt, wer der Eigentümer ist, deutet das auf ein Designproblem hin — kläre das, bevor du den Code schreibst.

Ein durchgearbeitetes Beispiel

Ein kurzes Programm, das einen String in eine In-Memory-Pipe kopiert und ihn durch einen gepufferten Reader zurückliest. Beide Streams sind AutoCloseable; beide werden automatisch geschlossen; wir fügen eine kleine benutzerdefinierte Ressource hinzu, damit du die Schließreihenfolge in der Ausgabe sehen kannst.

java— editable, runs on the server

In der Ausgabe ist zu sehen, dass die Deklarationsreihenfolge a, b, src, buf ist, die Schließreihenfolge jedoch umgekehrt ist: buf, src, b, a. Das ist die Sprachgarantie — zuerst geöffnet, zuletzt geschlossen.

Was als nächstes kommt

Bisher haben wir nur Ausnahmen abgefangen, die Java für uns geworfen hat. Echter Code muss auch eigene Ausnahmen auslösen — für ungültige Argumente, gebrochene Invarianten oder Domänenfehler. Weiter zu Java throw und throws.

Übungen

Übung
Ein `try-with-resources`-Block öffnet zwei `AutoCloseable`-Ressourcen. Der Body wirft eine `IOException`, und das `close()` der zweiten Ressource wirft ebenfalls eine `IOException`. Was sieht der Aufrufer?
Ein `try-with-resources`-Block öffnet zwei `AutoCloseable`-Ressourcen. Der Body wirft eine `IOException`, und das `close()` der zweiten Ressource wirft ebenfalls eine `IOException`. Was sieht der Aufrufer?
Was this page helpful?