Я пишу кусок кода:
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
Нужно ли мне закрывать каждый поток или писатель, как показано ниже?
gzipOutputStream.close();
bw.close();
outputStream.close();
Или можно просто закрыть последний поток?
bw.close();
java
file-io
outputstream
writer
Адон Смит
источник
источник
BufferedWriter
может потребоваться записать буферизованные данные в базовый поток, который в вашем примере уже закрыт. Избежание этих проблем является еще одним преимуществом подходов « попробуйте с ресурсами», показанных в ответах.Ответы:
Если предположить, что все потоки созданы нормально, да, просто закрытие
bw
подходит для этих реализаций потоков ; но это большое предположение.Я бы использовал try-with-resources ( учебник ), чтобы любые проблемы, связанные с построением последующих потоков, которые генерируют исключения, не оставляют предыдущие потоки зависшими, и поэтому вам не нужно полагаться на реализацию потока, имеющую вызов close основной поток:
Обратите внимание, что вы больше не звоните
close
.Важное примечание : чтобы попытка с ресурсами закрывала их, вы должны назначить потоки переменным по мере их открытия, вы не можете использовать вложение. Если вы используете вложение, исключение во время построения одного из последующих потоков (скажем,
GZIPOutputStream
) оставит открытым любой поток, созданный вложенными вызовами внутри него. Из JLS §14.20.3 :Обратите внимание на слово «переменные» (выделено мной) .
Например, не делайте этого:
... потому что исключение из
GZIPOutputStream(OutputStream)
конструктора (который говорит, что он может вызыватьIOException
и записывает заголовок в базовый поток) оставитFileOutputStream
открытым. Поскольку у одних ресурсов есть конструкторы, которые могут генерировать, а у других - нет, рекомендуется просто перечислять их отдельно.Мы можем дважды проверить нашу интерпретацию этого раздела JLS с помощью этой программы:
... который имеет вывод:
Обратите внимание, что звонков туда
close
нет.Если мы исправим
main
:тогда мы получаем соответствующие
close
вызовы:(Да, два вызова
InnerMost#close
- это правильно; один - отMiddle
, другой - от try-with-resources.)источник
java.io
. Некоторые потоки - обобщающие, некоторые ресурсы - выбрасываются из конструкторов. Так что, на мой взгляд, обеспечение того, чтобы несколько ресурсов открывались индивидуально, чтобы их можно было надежно закрыть при последующих выбросах ресурсов, - это просто хорошая привычка. Вы можете не делать этого, если не согласны, это нормально.GZIPOutputStream
конструктор записывает заголовок в поток. А так его можно и выкинуть. Итак, теперь вопрос в том, стоит ли, по-моему, попытаться закрыть поток после записи throw. Ага: Я открыл, надо хотя бы попытаться закрыть.Вы можете закрыть самый внешний поток, на самом деле вам не нужно сохранять все потоки в оболочке, и вы можете использовать Java 7 try-with-resources.
Если вы подписаны на YAGNI или вам это не понадобится, вам следует добавлять только тот код, который вам действительно нужен. Вы не должны добавлять код, который, по вашему мнению, может вам понадобиться, но на самом деле не делает ничего полезного.
Возьмите этот пример и представьте, что могло бы пойти не так, если бы вы этого не сделали, и каковы будут последствия?
Начнем с FileOutputStream, который
open
выполняет всю реальную работу.Если файл не найден, значит, нет базового ресурса, который нужно закрыть, поэтому его закрытие не имеет никакого значения. Если файл существует, он должен генерировать исключение FileNotFoundException. Таким образом, попытка закрыть ресурс только в этой строке ничего не даст.
Причина, по которой вам нужно закрыть файл, - это когда файл открывается успешно, но позже вы получаете сообщение об ошибке.
Давайте посмотрим на следующий поток
GZIPOutputStream
Есть код, который может вызвать исключение
Это записывает заголовок файла. Для вас было бы очень необычно иметь возможность открыть файл для записи, но не иметь возможности записать в него даже 8 байтов, но давайте представим, что это может произойти, и мы не закрываем файл после этого. Что произойдет с файлом, если он не закрыт?
Вы не получаете незаполненных записей, они отбрасываются, и в этом случае нет успешно записанных байтов в поток, который в любом случае не буферизуется в этот момент. Но файл, который не закрыт, не живет вечно, вместо этого FileOutputStream имеет
Если вы вообще не закрываете файл, он все равно закрывается, но не сразу (и, как я уже сказал, данные, оставшиеся в буфере, будут потеряны таким образом, но на данный момент их нет)
Каковы последствия немедленного закрытия файла? В нормальных условиях вы потенциально теряете некоторые данные и потенциально можете исчерпать файловые дескрипторы. Но если у вас есть система, в которой вы можете создавать файлы, но не можете в них ничего писать, у вас есть большая проблема. т.е. трудно представить, почему вы неоднократно пытаетесь создать этот файл, несмотря на то, что у вас ничего не получается.
И OutputStreamWriter, и BufferedWriter не генерируют исключение IOException в своих конструкторах, поэтому неясно, какую проблему они могут вызвать. В случае BufferedWriter вы можете получить OutOfMemoryError. В этом случае он немедленно запустит сборщик мусора, который, как мы видели, в любом случае закроет файл.
источник
GZIPOutputStream(OutputStream)
документыIOException
и, глядя на источник, фактически пишет заголовок. Так что это не теоретически, что конструктор может выбросить. Вы можете подумать, что оставлять базовый объектFileOutputStream
открытым после того, как написали в него, это нормально . Я не.Если все потоки были созданы, то закрытие только самого внешнего вполне нормально.
В документации по
Closeable
интерфейсу указан метод закрытия:Высвобождающие системные ресурсы включают закрывающие потоки.
В нем также говорится, что:
Поэтому, если вы потом явно закроете их, ничего плохого не произойдет.
источник
Я бы предпочел использовать
try(...)
синтаксис (Java 7), напримеристочник
Будет хорошо, если вы закроете только последний поток - вызов закрытия также будет отправлен в базовые потоки.
источник
Нет, самый верхний уровень
Stream
илиreader
гарантирует, что все базовые потоки / читатели будут закрыты.Проверьте реализацию
close()
метода вашего потока самого верхнего уровня.источник
В Java 7 есть возможность попробовать с ресурсами . Вам не нужно явно закрывать свои потоки, он позаботится об этом.
источник