Die Java 7- Try-with-Resources- Syntax (auch als ARM-Block ( Automatic Resource Management ) bezeichnet) ist nett, kurz und unkompliziert, wenn nur eine AutoCloseable
Ressource verwendet wird. Ich bin mir jedoch nicht sicher, was die richtige Redewendung ist, wenn ich mehrere voneinander abhängige Ressourcen deklarieren muss, z. B. a FileWriter
und a BufferedWriter
, die sie umschließen. Diese Frage betrifft natürlich jeden Fall, in dem einige AutoCloseable
Ressourcen eingeschlossen sind, nicht nur diese beiden spezifischen Klassen.
Ich habe mir die folgenden drei Alternativen ausgedacht:
1)
Die naive Redewendung, die ich gesehen habe, besteht darin, nur den Wrapper der obersten Ebene in der von ARM verwalteten Variablen zu deklarieren:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Das ist schön kurz, aber es ist kaputt. Da der Basiswert FileWriter
nicht in einer Variablen deklariert ist, wird er niemals direkt im generierten finally
Block geschlossen. Es wird nur durch die close
Methode des Wickelns geschlossen BufferedWriter
. Das Problem ist, dass wenn eine Ausnahme vom bw
Konstruktor des Konstruktors ausgelöst close
wird, diese nicht aufgerufen wird und daher der zugrunde liegende FileWriter
Wert nicht geschlossen wird .
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Hier werden sowohl die zugrunde liegende als auch die Wrapping-Ressource in den von ARM verwalteten Variablen deklariert, sodass beide sicher geschlossen werden, aber der zugrunde liegende fw.close()
wird zweimal aufgerufen : nicht nur direkt, sondern auch über den Wrapping bw.close()
.
Dies sollte kein Problem für diese beiden spezifischen Klassen sein, die beide implementieren Closeable
(was ein Subtyp von ist AutoCloseable
), deren Vertrag besagt, dass mehrere Aufrufe close
zulässig sind:
Schließt diesen Stream und gibt alle damit verbundenen Systemressourcen frei. Wenn der Stream bereits geschlossen ist, hat das Aufrufen dieser Methode keine Auswirkung.
Im Allgemeinen kann ich jedoch Ressourcen haben, die nur implementiert werden AutoCloseable
(und nicht Closeable
), was nicht garantiert, dass close
dies mehrmals aufgerufen werden kann:
Beachten Sie, dass diese close-Methode im Gegensatz zur close-Methode von java.io.Closeable nicht idempotent sein muss. Mit anderen Worten, das mehrmalige Aufrufen dieser close-Methode kann im Gegensatz zu Closeable.close, das bei mehrmaligem Aufruf keine Wirkung haben muss, sichtbare Nebenwirkungen haben. Implementierern dieser Schnittstelle wird jedoch dringend empfohlen, ihre engen Methoden idempotent zu machen.
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Diese Version sollte theoretisch korrekt sein, da nur die fw
eine echte Ressource darstellt, die bereinigt werden muss. Das bw
selbst enthält keine Ressource, es delegiert nur an das fw
, daher sollte es ausreichen, nur den Basiswert zu schließen fw
.
Andererseits ist die Syntax etwas unregelmäßig und Eclipse gibt eine Warnung aus, die meines Erachtens ein Fehlalarm ist, aber es ist immer noch eine Warnung, mit der man sich befassen muss:
Ressourcenleck: 'bw' wird niemals geschlossen
Also, welchen Ansatz sollte man wählen? Oder habe ich eine andere Redewendung verpasst, die die richtige ist ?
public BufferedWriter(Writer out, int sz)
kann man einen werfen IllegalArgumentException
. Außerdem kann ich BufferedWriter um eine Klasse erweitern, die etwas von ihrem Konstruktor abwirft oder einen benutzerdefinierten Wrapper erstellt, den ich benötige.
BufferedWriter
Konstruktor kann leicht eine Ausnahme auslösen. OutOfMemoryError
ist wahrscheinlich die häufigste, da sie dem Speicher einen angemessenen Speicherplatz zuweist (obwohl dies möglicherweise darauf hinweist, dass Sie den gesamten Prozess neu starten möchten). / Sie müssen flush
Ihre, BufferedWriter
wenn Sie nicht schließen und den Inhalt behalten möchten (in der Regel nur der Nicht-Ausnahmefall). FileWriter
nimmt auf, was auch immer die "Standard" -Dateicodierung ist - es ist besser, explizit zu sein.