Ответ 1
Предполагая, что все потоки создаются в порядке, да, только закрытие bw
отлично подходит для этих потоков; но это большое предположение.
Я бы использовал try-with-resources (tutorial), так что любые проблемы, связанные с созданием последующих потоков, которые генерируют исключения, не оставляют висящие предыдущие потоки, и поэтому вам не нужно полагаться на реализацию потока, имеющую вызов для закрытия базового потока:
try (
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
) {
// ...
}
Обратите внимание, что вы больше не вызываете close
.
Важное примечание. Чтобы закрыть их, вы должны назначить потоки переменным при их открытии, вы не можете использовать вложенность. Если вы используете вложенность, исключение при построении одного из более поздних потоков (скажем, GZIPOutputStream
) оставит поток, построенный вложенными в него внутренними вызовами. Из JLS §14.20.3:
Оператор try-with-resources параметризуется с помощью переменных (известных как ресурсы), которые инициализируются перед выполнением блока
try
и автоматически закрываются в обратном порядке, из которого они были инициализированы, после выполнения блокаtry
.
Обратите внимание на слово "переменные" (мое внимание).
Например, не делайте этого:
// DON'T DO THIS
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new GZIPOutputStream(
new FileOutputStream(createdFile))))) {
// ...
}
... потому что исключение из конструктора GZIPOutputStream(OutputStream)
(в котором говорится, что он может бросать IOException
) и записывает заголовок в базовый поток) оставит FileOutputStream
открытым. Поскольку у некоторых ресурсов есть конструкторы, которые могут бросать, а другие нет, хорошая привычка просто перечислять их отдельно.
Мы можем дважды проверить нашу интерпретацию этого раздела JLS с помощью этой программы:
public class Example {
private static class InnerMost implements AutoCloseable {
public InnerMost() throws Exception {
System.out.println("Constructing " + this.getClass().getName());
}
@Override
public void close() throws Exception {
System.out.println(this.getClass().getName() + " closed");
}
}
private static class Middle implements AutoCloseable {
private AutoCloseable c;
public Middle(AutoCloseable c) {
System.out.println("Constructing " + this.getClass().getName());
this.c = c;
}
@Override
public void close() throws Exception {
System.out.println(this.getClass().getName() + " closed");
c.close();
}
}
private static class OuterMost implements AutoCloseable {
private AutoCloseable c;
public OuterMost(AutoCloseable c) throws Exception {
System.out.println("Constructing " + this.getClass().getName());
throw new Exception(this.getClass().getName() + " failed");
}
@Override
public void close() throws Exception {
System.out.println(this.getClass().getName() + " closed");
c.close();
}
}
public static final void main(String[] args) {
// DON'T DO THIS
try (OuterMost om = new OuterMost(
new Middle(
new InnerMost()
)
)
) {
System.out.println("In try block");
}
catch (Exception e) {
System.out.println("In catch block");
}
finally {
System.out.println("In finally block");
}
System.out.println("At end of main");
}
}
... который имеет выход:
Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost In catch block In finally block At end of main
Обратите внимание, что там нет вызовов close
.
Если мы зафиксируем main
:
public static final void main(String[] args) {
try (
InnerMost im = new InnerMost();
Middle m = new Middle(im);
OuterMost om = new OuterMost(m)
) {
System.out.println("In try block");
}
catch (Exception e) {
System.out.println("In catch block");
}
finally {
System.out.println("In finally block");
}
System.out.println("At end of main");
}
то мы получим соответствующие вызовы close
:
Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost Example$Middle closed Example$InnerMost closed Example$InnerMost closed In catch block In finally block At end of main
(Да, два вызова InnerMost#close
верны: один из Middle
, другой из try-with-resources.)