Saya menulis sepotong kode:
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
Apakah saya perlu menutup setiap aliran atau penulis seperti yang berikut ini?
gzipOutputStream.close();
bw.close();
outputStream.close();
Atau hanya akan menutup aliran terakhir baik-baik saja?
bw.close();
java
file-io
outputstream
writer
Adon Smith
sumber
sumber
BufferedWriter
, mungkin perlu menulis data buffer ke aliran yang mendasarinya, yang dalam contoh Anda sudah ditutup. Menghindari masalah ini adalah keuntungan lain dari pendekatan coba sumber daya yang ditunjukkan dalam jawaban.Jawaban:
Dengan asumsi semua aliran bisa dibuat baik-baik saja, ya, hanya menutup
bw
baik - baik saja dengan implementasi aliran itu ; tapi itu asumsi besar.Saya akan menggunakan try-with-resources ( tutorial ) sehingga masalah apa pun yang membangun aliran berikutnya yang melempar pengecualian tidak membuat aliran sebelumnya menggantung, dan jadi Anda tidak harus bergantung pada implementasi aliran yang membuat panggilan untuk ditutup aliran yang mendasarinya:
Perhatikan Anda tidak lagi menelepon
close
sama sekali.Catatan penting : Agar sumber daya coba tutup, Anda harus menetapkan aliran ke variabel saat Anda membukanya, Anda tidak dapat menggunakan bersarang. Jika Anda menggunakan nesting, pengecualian selama pembangunan salah satu aliran yang lebih baru (katakanlah,
GZIPOutputStream
) akan membuat aliran apa pun yang dibangun oleh panggilan bersarang di dalamnya terbuka. Dari JLS §14.20.3 :Perhatikan kata "variabel" (penekanan saya) .
Misalnya, jangan lakukan ini:
... karena pengecualian dari
GZIPOutputStream(OutputStream)
konstruktor (yang mengatakan mungkin melemparIOException
, dan menulis header ke aliran yang mendasarinya) akan membiarkanFileOutputStream
terbuka. Karena beberapa sumber memiliki konstruktor yang dapat melempar dan yang lain tidak, itu kebiasaan yang baik untuk hanya daftar secara terpisah.Kami dapat memeriksa ulang interpretasi kami atas bagian JLS itu dengan program ini:
... yang memiliki output:
Perhatikan bahwa tidak ada panggilan ke
close
sana.Jika kami memperbaiki
main
:maka kami mendapatkan
close
panggilan yang sesuai :(Ya, dua panggilan untuk
InnerMost#close
itu benar; satu dariMiddle
, yang lain dari coba-dengan-sumber daya.)sumber
java.io
. Beberapa aliran - generalisasi, beberapa sumber daya - membuang dari konstruktor. Jadi, memastikan beberapa sumber daya dibuka secara individual sehingga mereka dapat ditutup andal jika sumber daya berikutnya hanya merupakan kebiasaan yang baik, dalam pandangan saya. Anda dapat memilih untuk tidak melakukannya jika Anda tidak setuju, itu tidak masalah.GZIPOutputStream
konstruktor menulis header ke stream. Dan itu bisa melempar. Jadi sekarang posisinya adalah apakah saya pikir layak untuk mencoba untuk menutup aliran setelah penulisan melemparkan. Ya: Saya membukanya, setidaknya saya harus mencoba untuk menutupnya.Anda dapat menutup aliran paling luar, bahkan Anda tidak perlu mempertahankan semua aliran yang dibungkus dan Anda dapat menggunakan Java 7 coba-dengan-sumber daya.
Jika Anda berlangganan YAGNI, atau Anda -tidak akan membutuhkannya, Anda seharusnya hanya menambahkan kode yang benar-benar Anda butuhkan. Anda seharusnya tidak menambahkan kode yang Anda bayangkan mungkin Anda butuhkan tetapi pada kenyataannya tidak melakukan sesuatu yang bermanfaat.
Ambil contoh ini dan bayangkan apa yang mungkin salah jika Anda tidak melakukan ini dan apa dampaknya?
Mari kita mulai dengan FileOutputStream yang memanggil
open
untuk melakukan semua pekerjaan nyata.Jika file tidak ditemukan, tidak ada sumber daya yang mendasarinya untuk ditutup, jadi menutupnya tidak akan membuat perbedaan. Jika file ada, itu harus membuang FileNotFoundException. Jadi tidak ada yang bisa diperoleh dengan mencoba menutup sumber daya dari garis ini saja.
Alasan Anda perlu menutup file adalah ketika file dibuka dengan sukses, tetapi Anda kemudian mendapatkan kesalahan.
Mari kita lihat aliran selanjutnya
GZIPOutputStream
Ada kode yang bisa melempar pengecualian
Ini menulis header file. Sekarang akan sangat tidak biasa bagi Anda untuk dapat membuka file untuk menulis tetapi tidak dapat menulis bahkan 8 byte untuk itu, tetapi mari kita bayangkan ini bisa terjadi dan kami tidak menutup file setelahnya. Apa yang terjadi pada file jika tidak ditutup?
Anda tidak mendapatkan tulisan yang tidak disiram, mereka dibuang dan dalam hal ini, tidak ada byte yang berhasil ditulis ke aliran yang tidak buffer pada saat ini. Tetapi file yang tidak ditutup tidak hidup selamanya, sebaliknya FileOutputStream memiliki
Jika Anda tidak menutup file sama sekali, file tetap akan ditutup, tidak segera (dan seperti saya katakan, data yang tersisa di buffer akan hilang dengan cara ini, tetapi tidak ada pada saat ini)
Apa konsekuensi dari tidak segera menutup file? Dalam kondisi normal, Anda berpotensi kehilangan beberapa data, dan Anda berpotensi kehabisan file deskriptor. Tetapi jika Anda memiliki sistem di mana Anda dapat membuat file tetapi Anda tidak dapat menulis apa pun kepada mereka, Anda memiliki masalah lebih besar. yaitu sulit membayangkan mengapa Anda berulang kali mencoba membuat file ini meskipun Anda gagal.
Baik OutputStreamWriter dan BufferedWriter tidak melempar IOException ke konstruktor mereka, jadi tidak jelas masalah apa yang akan mereka sebabkan. Dalam kasus BufferedWriter, Anda bisa mendapatkan OutOfMemoryError. Dalam hal ini akan segera memicu GC, yang seperti telah kita lihat akan menutup file.
sumber
GZIPOutputStream(OutputStream)
dokumenIOException
dan, melihat sumbernya, sebenarnya menulis header. Jadi bukan teori, bahwa konstruktor dapat melempar. Anda mungkin merasa tidak apa-apa untuk membiarkan dasarnyaFileOutputStream
terbuka setelah menulis. Bukan saya.Jika semua aliran telah dipakai maka menutup hanya yang terluar saja.
Dokumentasi pada
Closeable
antarmuka menyatakan bahwa metode tutup:Sumber daya sistem pelepasan mencakup aliran penutup.
Ini juga menyatakan bahwa:
Jadi jika Anda menutupnya secara eksplisit setelah itu, tidak ada yang salah akan terjadi.
sumber
Saya lebih suka menggunakan
try(...)
sintaks (Java 7), missumber
Ini akan baik-baik saja jika Anda hanya menutup aliran terakhir - panggilan dekat juga akan dikirimkan ke aliran yang mendasarinya.
sumber
Tidak, tingkat paling atas
Stream
ataureader
akan memastikan bahwa semua aliran / pembaca yang mendasarinya ditutup.Periksa implementasi
close()
metode aliran level teratas Anda.sumber
Di Java 7, ada fitur try-with-resources . Anda tidak perlu menutup aliran secara eksplisit, itu akan mengatasi hal itu.
sumber