java - 是否需要分别关闭每个嵌套的 OutputStream 和 Writer?

标签 java file-io outputstream writer

我正在写一段代码:

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 the try 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/

相关文章:

JavaMail - Java 应用程序不接收文件夹事件

java - 下载大文件太慢

Java泛型类构造函数调用

java - 更改 Java 中返回的对象

python - 如何在 C 中的文件名后增加数字?

python - 检查文件是否存在失败

java - Spring:已经为此响应调用了 getOutputStream()

c - 这个简单的程序是如何工作的,与 I/O 相关

java - 在java中打印字符串字符下的星号(*)

c - 如何在 C 编程中读取文件?