Ich schreibe einen Code:
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
Muss ich jeden Stream oder Writer wie folgt schließen?
gzipOutputStream.close();
bw.close();
outputStream.close();
Oder ist es in Ordnung, nur den letzten Stream zu schließen?
bw.close();
java
file-io
outputstream
writer
Adon Smith
quelle
quelle
BufferedWriter
möglicherweise gepufferte Daten in den zugrunde liegenden Stream geschrieben werden, der in Ihrem Beispiel bereits geschlossen ist. Das Vermeiden dieser Probleme ist ein weiterer Vorteil der in den Antworten gezeigten Try-with-Resource- Ansätze.Antworten:
Angenommen, alle Streams werden in Ordnung erstellt. Ja, nur das Schließen
bw
ist für diese Stream-Implementierungen in Ordnung . aber das ist eine große Annahme.Ich würde Try-with-Resources ( Tutorial ) verwenden, damit beim Erstellen der nachfolgenden Streams, die Ausnahmen auslösen, die vorherigen Streams nicht hängen bleiben und Sie sich nicht darauf verlassen müssen, dass die Stream-Implementierung den Aufruf zum Schließen hat der zugrunde liegende Stream:
Beachten Sie, dass Sie überhaupt nicht mehr anrufen
close
.Wichtiger Hinweis : Damit Try-with-Resources sie schließen kann, müssen Sie die Streams beim Öffnen Variablen zuweisen. Sie können die Verschachtelung nicht verwenden. Wenn Sie die Verschachtelung verwenden, lässt eine Ausnahme während der Erstellung eines der späteren Streams (z. B.
GZIPOutputStream
) jeden Stream offen, der durch die darin enthaltenen verschachtelten Aufrufe erstellt wurde. Aus JLS §14.20.3 :Beachten Sie das Wort "Variablen" (meine Betonung) .
ZB mach das nicht:
... weil eine Ausnahme vom
GZIPOutputStream(OutputStream)
Konstruktor (der besagt, dass er möglicherweise ausgelöstIOException
wird und einen Header in den zugrunde liegenden Stream schreibt) dasFileOutputStream
offen lassen würde. Da einige Ressourcen Konstruktoren haben, die möglicherweise ausgelöst werden, andere jedoch nicht, ist es eine gute Angewohnheit, sie nur separat aufzulisten.Mit diesem Programm können wir unsere Interpretation dieses JLS-Abschnitts überprüfen:
... was die Ausgabe hat:
Beachten Sie, dass dort keine Anrufe
close
eingehen.Wenn wir Folgendes beheben
main
:dann bekommen wir die entsprechenden
close
Anrufe:(Ja, zwei Aufrufe von
InnerMost#close
sind korrekt; einer stammt vonMiddle
, der andere von try-with-resources.)quelle
java.io
. Einige Streams - verallgemeinernd, einige Ressourcen - werden von Konstruktoren ausgegeben. Daher ist es meiner Ansicht nach nur eine gute Angewohnheit, sicherzustellen, dass mehrere Ressourcen einzeln geöffnet werden, damit sie zuverlässig geschlossen werden können, wenn eine nachfolgende Ressource ausgelöst wird. Sie können sich dafür entscheiden, es nicht zu tun, wenn Sie nicht einverstanden sind, das ist in Ordnung.GZIPOutputStream
Der Konstruktor schreibt einen Header in den Stream. Und so kann es werfen. Nun ist die Position also, ob es sich meiner Meinung nach lohnt, zu versuchen , den Stream nach dem Schreiben zu schließen. Ja, ich habe es geöffnet, ich sollte zumindest versuchen, es zu schließen.Sie können den äußersten Stream schließen. Tatsächlich müssen Sie nicht alle umschlossenen Streams beibehalten, und Sie können Java 7 Try-with-Resources verwenden.
Wenn Sie YAGNI abonnieren oder es nicht brauchen, sollten Sie nur den Code hinzufügen, den Sie tatsächlich benötigen. Sie sollten keinen Code hinzufügen, von dem Sie sich vorstellen, dass Sie ihn benötigen, aber in Wirklichkeit tun Sie nichts Nützliches.
Nehmen Sie dieses Beispiel und stellen Sie sich vor, was möglicherweise schief gehen könnte, wenn Sie dies nicht tun würden und welche Auswirkungen dies hätte.
Beginnen wir mit FileOutputStream, der aufruft,
open
um die gesamte eigentliche Arbeit zu erledigen.Wenn die Datei nicht gefunden wird, muss keine zugrunde liegende Ressource geschlossen werden, sodass das Schließen keinen Unterschied macht. Wenn die Datei vorhanden ist, sollte eine FileNotFoundException ausgelöst werden. Es gibt also nichts zu gewinnen, wenn versucht wird, die Ressource allein aus dieser Zeile zu schließen.
Der Grund, warum Sie die Datei schließen müssen, ist, wenn die Datei erfolgreich geöffnet wurde, aber später eine Fehlermeldung angezeigt wird.
Schauen wir uns den nächsten Stream an
GZIPOutputStream
Es gibt Code, der eine Ausnahme auslösen kann
Dies schreibt den Header der Datei. Jetzt wäre es sehr ungewöhnlich, dass Sie eine Datei zum Schreiben öffnen können, aber nicht einmal 8 Bytes darauf schreiben können. Stellen Sie sich jedoch vor, dass dies passieren könnte und wir die Datei danach nicht schließen. Was passiert mit einer Datei, wenn sie nicht geschlossen ist?
Sie erhalten keine nicht geleerten Schreibvorgänge, sie werden verworfen, und in diesem Fall gibt es keine erfolgreich geschriebenen Bytes in den Stream, die zu diesem Zeitpunkt ohnehin nicht gepuffert sind. Aber eine Datei, die nicht geschlossen ist, lebt nicht für immer, sondern FileOutputStream
Wenn Sie eine Datei überhaupt nicht schließen, wird sie trotzdem geschlossen, nur nicht sofort (und wie gesagt, Daten, die in einem Puffer verbleiben, gehen auf diese Weise verloren, aber zu diesem Zeitpunkt gibt es keine).
Was ist die Folge davon, dass die Datei nicht sofort geschlossen wird? Unter normalen Bedingungen verlieren Sie möglicherweise einige Daten, und Ihnen gehen möglicherweise die Dateideskriptoren aus. Wenn Sie jedoch ein System haben, in dem Sie Dateien erstellen, aber nichts darauf schreiben können, haben Sie ein größeres Problem. Das heißt, es ist schwer vorstellbar, warum Sie wiederholt versuchen, diese Datei zu erstellen, obwohl Sie einen Fehler machen.
Sowohl OutputStreamWriter als auch BufferedWriter lösen keine IOException in ihren Konstruktoren aus, sodass nicht klar ist, welches Problem sie verursachen würden. Im Fall von BufferedWriter könnten Sie einen OutOfMemoryError erhalten. In diesem Fall wird sofort ein GC ausgelöst, der, wie wir gesehen haben, die Datei trotzdem schließt.
quelle
GZIPOutputStream(OutputStream)
DokumenteIOException
und schreibt, wenn man sich die Quelle ansieht, tatsächlich einen Header. Es ist also nicht theoretisch, dass der Konstruktor werfen kann. Möglicherweise ist es in Ordnung, den zugrunde liegendenFileOutputStream
Wert nach dem Schreiben offen zu lassen. Ich nicht.Wenn alle Streams instanziiert wurden, ist es in Ordnung, nur den äußersten zu schließen.
In der Dokumentation zur
Closeable
Schnittstelle heißt es: Schließen Sie die Methode:Das Freigeben von Systemressourcen umfasst das Schließen von Streams.
Es heißt auch, dass:
Wenn Sie sie also später explizit schließen, passiert nichts Falsches.
quelle
Ich würde lieber
try(...)
Syntax (Java 7) verwenden, zquelle
Es ist in Ordnung, wenn Sie nur den letzten Stream schließen - der Abschlussaufruf wird auch an die zugrunde liegenden Streams gesendet.
quelle
Nein, die oberste Ebene
Stream
oder stelltreader
sicher, dass alle zugrunde liegenden Streams / Reader geschlossen sind.Überprüfen Sie die
close()
Methode Umsetzung Ihrer obersten Ebene Stream.quelle
In Java 7 gibt es eine Funktion zum Ausprobieren von Ressourcen . Sie müssen Ihre Streams nicht explizit schließen, das wird erledigt.
quelle