问题描述
我写了一段代码: 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
是很好的 ;但这是一个很大的假设。
我会使用(),这样任何构造引发异常的后续流的问题都不会使前面的流挂起来,所以你不必依赖流实现调用以关闭底层流:
$ p $ try(
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
){
// ...
$ / code>
请注意,您不再调用
close
。\\ b
$ b
重要提示:为了让try-with-resources关闭它们,您必须在打开它们时将这些流分配给变量,不能使用嵌套。如果使用嵌套,则在构建其中一个后续流(例如,
GZIPOutputStream
)时发生的异常将使任何由嵌套调用构造的流都处于打开状态。从构造函数(它可能会抛出 IOException
,并将一个头写入底层的流)将打开 FileOutputStream
。由于某些资源的构造函数可能会抛出,而其他资源则不会抛出,所以单独列出它们是个好习惯。
我们可以仔细检查一下我们对JLS段的解释使用这个程序:
public class Example {
private static class InnerMost implements AutoCloseable {
public InnerMost()抛出异常{
System.out.println(构造+ this.getClass()。getName());
$ b @Override $ b $ public void close()throws Exception {
System.out.println(this.getClass()。getName()+closed );
private static class中间实现AutoCloseable {
private AutoCloseable c;
$ b public public(AutoCloseable c){
System.out.println(Constructing+ this.getClass()。getName());
this.c = c;
$ b @Override $ b $ public void close()throws Exception {
System.out.println(this.getClass()。getName()+closed );
c.close();
private static class OuterMost implements AutoCloseable {
private AutoCloseable c;
$ b $ public OuterMost(AutoCloseable c)throws Exception {
System.out.println(Constructing+ this.getClass()。getName());
抛出新的异常(this.getClass()。getName()+failed);
$ b @Override $ b $ public void close()throws Exception {
System.out.println(this.getClass()。getName()+closed );
c.close();
$ b $ public static final(String [] args){
//不要做这个
try(OuterMost om =新的OuterMost(
新的中间(
新InnerMost()
)
)
){
System.out.println(在try块) ;
catch(Exception e){
System.out.println(catch catch);
}
finally {
System.out.println(In finally block);
}
System.out.println(在主结束时);
$ b $ p $有输出:
构造示例$ InnerMost
构造示例$ Middle
构造示例$ OuterMost
在catch块
在finally块
$ b 注意,没有调用 close
there。
如果我们修正 main
:
$ b $ (
InnerMost im = new InnerMost();
Middle m =(
$ p $) (im);
OuterMost om = new OuterMost(m)
){
System.out.println(In try block);
catch(Exception e){
System.out.println(catch catch);
}
finally {
System.out.println(In finally block);
}
System.out.println(在主结束时);
$ / code>
然后我们得到相应的 close
$ b构造示例$ InnerMost
构造示例$ Middle
构造示例$ OuterMost
示例$中间关闭
示例$ InnerMost关闭
示例$ InnerMost关闭
在catch块
中finally块
在主
InnerMost#close
是正确的;一个是从中间的
,另一个来自try-with-resources。)
I am writing a piece of code:
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
Do I need to close every stream or writer like the following?
gzipOutputStream.close();
bw.close();
outputStream.close();
Or will just closing the last stream be fine?
bw.close();
解决方案 Assuming all the streams get created okay, yes, just closing bw
is fine with those stream implementations; but that's a big assumption.
I'd use try-with-resources (tutorial) so that any issues constructing the subsequent streams that throw exceptions don't leave the previous streams hanging, and so you don't have to rely on the stream implementation having the call to close the underlying stream:
try (
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
) {
// ...
}
Note you no longer call close
at all.
Important note: To have try-with-resources close them, you must assign the streams to variables as you open them, you cannot use nesting. If you use nesting, an exception during construction of one of the later streams (say, GZIPOutputStream
) will leave any stream constructed by the nested calls inside it open. From JLS §14.20.3:
Note the word "variables" (my emphasis).
E.g., don't do this:
// DON'T DO THIS
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new GZIPOutputStream(
new FileOutputStream(createdFile))))) {
// ...
}
...because an exception from the GZIPOutputStream(OutputStream)
constructor (which says it may throw IOException
, and writes a header to the underlying stream) would leave the FileOutputStream
open. Since some resources have constructors that may throw and others don't, it's a good habit to just list them separately.
We can double-check our interpretation of that JLS section with this program:
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");
}
}
...which has the output:
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");
}
then we get the appropriate close
calls:
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
(Yes, two calls to InnerMost#close
is correct; one is from Middle
, the other from try-with-resources.)
这篇关于是否有必要分别关闭每个嵌套的OutputStream和Writer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!