是否有必要分别关闭每个嵌套的 OutputStream 和 Writer? [英] Is it necessary to close each nested OutputStream and Writer separately?

查看:30
本文介绍了是否有必要分别关闭每个嵌套的 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 就可以了那些流实现;但这是一个很大的假设.

我会使用 尝试-with-resources(tutorial)以便任何构建引发异常的后续流的问题不会使先前的流挂起,因此您不必依赖具有调用关闭底层流的流实现:

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

请注意,您根本不再调用 close.

重要说明:要让 try-with-resources 关闭它们,必须在打开它们时将流分配给变量,不能使用嵌套.如果您使用嵌套,则在构建其中一个流(例如,GZIPOutputStream)期间的异常将使由其内部的嵌套调用构建的任何流保持打开状态.来自 JLS §14.20.3:

<块引用>

try-with-resources 语句使用变量(称为资源)参数化,这些变量在执行 try 块之前初始化并以相反的顺序自动关闭在执行 try 块后,它们被初始化.

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

例如,不要这样做:

//不要这样做尝试 (BufferedWriter bw = new BufferedWriter(新的输出流写入器(新的 GZIPOutputStream(新的 FileOutputStream(createdFile))))) {//...}

...因为 GZIPOutputStream(OutputStream) 构造函数(它说它可能会抛出 IOException,并将头写入底层流)保持 FileOutputStream 打开.由于某些资源具有可能抛出的构造函数而其他资源没有,因此将它们分开列出是一个好习惯.

我们可以用这个程序仔细检查我们对 JLS 部分的解释:

公共类示例{私有静态类 InnerMost 实现 AutoCloseable {公共 InnerMost() 抛出异常 {System.out.println("构造" + this.getClass().getName());}@覆盖public void close() 抛出异常 {System.out.println(this.getClass().getName() + "关闭");}}私有静态类 Middle 实现 AutoCloseable {私人 AutoCloseable c;公共中间(AutoCloseable c){System.out.println("构造" + this.getClass().getName());this.c = c;}@覆盖public void close() 抛出异常 {System.out.println(this.getClass().getName() + "关闭");c.关闭();}}私有静态类 OuterMost 实现 AutoCloseable {私人 AutoCloseable c;公共 OuterMost(AutoCloseable c) 抛出异常 {System.out.println("构造" + this.getClass().getName());throw new Exception(this.getClass().getName() + " failed");}@覆盖public void close() 抛出异常 {System.out.println(this.getClass().getName() + "关闭");c.关闭();}}public static final void main(String[] args) {//不要这样做试试 (OuterMost om = new OuterMost(新中(new InnerMost()))){System.out.println("在try块中");}捕获(异常 e){System.out.println("在catch块中");}最后 {System.out.println("在finally块中");}System.out.println("主线程结束");}}

...输出:

<前>构造示例$InnerMost构造示例$Middle构造示例$OuterMost在 catch 块中在 finally 块中在主要结束时

请注意,那里没有对 close 的调用.

如果我们修复main:

public static final void main(String[] args) {尝试 (InnerMost im = new InnerMost();中间 m = 新中间 (im);OuterMost om = 新 OuterMost(m)){System.out.println("在try块中");}捕获(异常 e){System.out.println("在catch块中");}最后 {System.out.println("在finally块中");}System.out.println("主线程结束");}

然后我们得到适当的close调用:

<前>构造示例$InnerMost构造示例$Middle构造示例$OuterMost示例$Middle 关闭示例$InnerMost 关闭示例$InnerMost 关闭在 catch 块中在 finally 块中在主要结束时

(是的,对 InnerMost#close 的两次调用是正确的;一个来自 Middle,另一个来自 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:

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.

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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆