我正在写一段代码:
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();
最佳答案
假设所有的流都创建好了,是的,只需关闭 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
。
重要提示:要让 try-with-resources 关闭它们,您必须在打开流时将流分配给变量,不能使用嵌套。如果您使用嵌套,则在构建后面的流之一(例如,GZIPOutputStream
)期间的异常将使由其中的嵌套调用构造的任何流保持打开状态。来自 JLS §14.20.3 :
A try-with-resources statement is parameterized with variables (known as resources) that are initialized before execution of the
try
block and closed automatically, in the reverse order from which they were initialized, after execution of thetry
block.
注意“变量”这个词(我的重点)。
例如,不要这样做:
// DON'T DO THIS
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new GZIPOutputStream(
new FileOutputStream(createdFile))))) {
// ...
}
...因为 GZIPOutputStream(OutputStream)
的异常构造函数(表示它可能抛出 IOException
,并将 header 写入底层流)将使 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
Note that there are no calls to close
there.
If we fix 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。)
关于java - 是否需要分别关闭每个嵌套的 OutputStream 和 Writer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28276423/