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

2023-11-27

我正在写一段代码:

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很好与这些流实现;但这是一个很大的假设。

I'd use 尝试资源 (tutorial)这样构造引发异常的后续流的任何问题都不会导致先前的流挂起,因此您不必依赖具有调用来关闭基础流的流实现:

try (
    OutputStream outputStream = new FileOutputStream(createdFile);
    GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
    OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
    BufferedWriter bw = new BufferedWriter(osw)
    ) {
    // ...
}

请注意您不再打电话close at all.

重要的提示:要让 try-with-resources 关闭它们,您must在打开流时将流分配给变量,不能使用嵌套。如果您使用嵌套,则在构造后续流之一期间会出现异常(例如,GZIPOutputStream) 将使由其中的嵌套调用构造的任何流保持打开状态。从JLS §14.20.3:

try-with-resources 语句的参数化为变量(称为资源)在执行之前初始化try执行后,按照与初始化相反的顺序自动阻止并关闭try block.

注意“变量”这个词(我的重点).

例如,不要这样做:

// 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 there.

如果我们修复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 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
  

(是的,两次致电InnerMost#close是正确的;一个来自Middle,另一个来自 try-with-resources。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否需要分别关闭每个嵌套的OutputStream和Writer? 的相关文章

随机推荐