是否有必要分别closures每个嵌套的OutputStream和Writer?
我正在写一段代码:
OutputStream outputStream = new FileOutputStream(createdFile); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
我是否需要closures每个stream或作者如下?
gzipOutputStream.close(); bw.close(); outputStream.close();
或者只是closures最后一个stream是好的?
bw.close();
假设所有的stream都被创build好了,是的,只要closuresbw
就可以使用这些stream实现 。 但这是一个很大的假设。
我将使用try-with-resources ( tutorial ),以便构造引发exception的后续stream的任何问题都不会使先前的stream停止,因此您不必依靠调用closures的stream实现基础stream:
try ( OutputStream outputStream = new FileOutputStream(createdFile); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream); BufferedWriter bw = new BufferedWriter(osw) ) { // ... }
请注意,您不再致电close
。
重要提示 :要使用资源尝试closures它们,您必须在打开它们时将这些stream分配给variables,但不能使用嵌套。 如果使用嵌套,则在构build其中一个后续stream(例如GZIPOutputStream
)期间发生的exception将使任何由嵌套调用构造的stream都处于打开状态。 根据JLS§14.20.3 :
try-with-resources语句是用variables (称为资源)参数化的,它们在执行
try
块之前被初始化,并且在执行try
块之后按照与它们初始化相反的顺序自动closures。
请注意“variables” (我强调) 。
例如,不要这样做:
// DON'T DO THIS try (BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( new GZIPOutputStream( new FileOutputStream(createdFile))))) { // ... }
…因为来自GZIPOutputStream(OutputStream)
构造函数(表示可能抛出IOException
,并将头部写入基础stream)的exception将使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"); } }
有输出的:
构造示例$ InnerMost 构造示例$中 构造示例$ OuterMost 在catch块 在终于阻止 在主要结束
请注意,没有电话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
电话:
构造示例$ InnerMost 构造示例$中 构造示例$ OuterMost 示例$中间closures 示例$ InnerMostclosures 示例$ InnerMostclosures 在catch块 在终于阻止 在主要结束
(是的,关于InnerMost#close
两个调用是正确的,一个来自Middle
,另一个来自试用资源。)
你可以closures最外层的stream,实际上你不需要保留所有的stream,你可以使用Java 7的try-with-resources。
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( new GZIPOutputStream(new FileOutputStream(createdFile)))) { // write to the buffered writer }
如果您订阅YAGNI,或者您不需要,您应该只添加您实际需要的代码。 你不应该添加你认为可能需要的代码,但实际上并没有做任何有用的事情。
拿这个例子来想象一下,如果你不这样做,会产生什么样的影响呢?
try ( OutputStream outputStream = new FileOutputStream(createdFile); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream); BufferedWriter bw = new BufferedWriter(osw) ) { // ... }
让我们从调用open
FileOutputStream开始做所有的实际工作。
/** * Opens a file, with the specified name, for overwriting or appending. * @param name name of file to be opened * @param append whether the file is to be opened in append mode */ private native void open(String name, boolean append) throws FileNotFoundException;
如果没有find该文件,则没有底层资源closures,因此closures该文件不会有任何区别。 如果该文件存在,它应该抛出一个FileNotFoundException。 所以试图从这条线上单独closures资源是没有任何好处的。
您需要closures文件的原因是文件成功打开,但以后出现错误。
让我们看看下一个streamGZIPOutputStream
有代码可以抛出exception
private void writeHeader() throws IOException { out.write(new byte[] { (byte) GZIP_MAGIC, // Magic number (short) (byte)(GZIP_MAGIC >> 8), // Magic number (short) Deflater.DEFLATED, // Compression method (CM) 0, // Flags (FLG) 0, // Modification time MTIME (int) 0, // Modification time MTIME (int) 0, // Modification time MTIME (int) 0, // Modification time MTIME (int) 0, // Extra flags (XFLG) 0 // Operating system (OS) }); }
这写入文件的标题。 现在打开一个文件进行写入,但是不能写8个字节的文件是非常不寻常的,但是让我们想象这会发生,然后我们不closures文件。 如果文件没有closures,会发生什么?
你没有得到任何未刷新的写入,它们被丢弃,在这种情况下,没有成功写入字节到这个时候没有被caching的stream。 但是一个没有closures的文件并不是永远的,而是FileOutputStream
protected void finalize() throws IOException { if (fd != null) { if (fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else { /* if fd is shared, the references in FileDescriptor * will ensure that finalizer is only called when * safe to do so. All references using the fd have * become unreachable. We can call close() */ close(); } } }
如果你根本没有closures文件,它就会被closures,而不是立即(就像我说的那样,留在缓冲区中的数据将会以这种方式丢失,但是在这一点上是没有的)
什么是不立即closures文件的后果? 在正常情况下,您可能会丢失一些数据,并有可能用完文件描述符。 但是如果你有一个系统可以创build文件,但是你不能写任何东西给他们,那么你就有更大的问题。 即很难想象,为什么你反复尝试创build这个文件,尽pipe你失败了。
OutputStreamWriter和BufferedWriter都不会在其构造函数中抛出IOException,所以不清楚它们会造成什么问题。 在BufferedWriter的情况下,你可能会得到一个OutOfMemoryError。 在这种情况下,它会立即触发一个GC,正如我们所看到的,无论如何将closures文件。
如果所有的stream都被实例化了,那么只closures最外层就好了。
可closures接口上的文档声明closures方法:
closures此stream并释放与其关联的所有系统资源。
释放系统资源包括closuresstream。
它还指出:
如果stream已经closures,则调用此方法不起作用。
所以如果你事后明确地closures它们,没有什么不对的。
我宁愿使用try(...)
语法(Java 7),例如
try (OutputStream outputStream = new FileOutputStream(createdFile)) { ... }
如果您只closures最后一个stream,那么也可以,closures呼叫也会发送到基础stream。
不,最顶层的Stream
或reader
将确保所有基础stream/读取器都closures。
检查最顶层的stream的close()
方法实现 。
在Java 7中,有一个function尝试与资源 。 你不需要明确closures你的stream,它会照顾到这一点。