26

我在 try/catch 块中有以下代码

 InputStream inputstream = conn.getInputStream();
 InputStreamReader inputstreamreader = new  InputStreamReader(inputstream);
 BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

我的问题是,当我必须在 finally 块中关闭这些流时,我是否必须关闭所有 3 个流或仅关闭befferedreader会关闭所有其他流?

4

6 回答 6

29

按照惯例,包装流(包装现有流)在关闭底层流时会关闭它们,因此只需要bufferedreader在您的示例中关闭即可。此外,关闭已经关闭的流通常是无害的,因此关闭所有 3 个流也不会受到伤害。

于 2012-06-29T14:47:10.457 回答
5

通常只关闭最外层的流是可以的,因为按照惯例,它必须在底层流上触发关闭。

所以通常代码如下所示:

BufferedReader in = null;

try {
    in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    ...
    in.close(); // when you care about Exception-Handling in case when closing fails
}
finally {
    IOUtils.closeQuietly(in); // ensure closing; Apache Commons IO
}

然而,在极少数情况下,底层流构造函数会在流已经打开的情况下引发异常。在这种情况下,上面的代码不会关闭底层流,因为外部构造函数从未被调用并且in为空。因此,finally 块不会关闭任何使底层流打开的东西。

从 Java 7 开始,您可以这样做:

    try (OutputStream out1 = new ...; OutputStream out2 = new ...) {
        ...
        out1.close(); //if you want Exceptions-Handling; otherwise skip this
        out2.close(); //if you want Exceptions-Handling; otherwise skip this            
    } // out1 and out2 are auto-closed when leaving this block

在大多数情况下,您不希望在关闭时引发异常处理,因此请跳过这些显式的 close() 调用。

编辑 这里有一些代码供非信徒使用,使用这种模式很重要。您可能还想阅读有关closeQuietly()方法的 Apache Commons IOUtils javadoc。

    OutputStream out1 = null;
    OutputStream out2 = null;

    try {
        out1 = new ...;
        out2 = new ...;

        ...

        out1.close(); // can be skipped if we do not care about exception-handling while closing
        out2.close(); // can be skipped if we ...
    }
    finally {
        /*
         * I've some custom methods in my projects overloading these
         * closeQuietly() methods with a 2nd param taking a logger instance, 
         * because usually I do not want to react on Exceptions during close 
         * but want to see it in the logs when it happened.
         */
        IOUtils.closeQuietly(out1);
        IOUtils.closeQuietly(out2);
    }

out1当创建out2引发异常时,使用@Tom 的“建议”将保持打开状态。这个建议来自有人谈论It's a continual source of errors for obvious reasons.好吧,我可能是盲人,但这对我来说并不明显。我的模式在我能想到的每个用例中都是白痴安全的,而 Tom 的模式容易出错。

于 2012-06-29T14:55:25.057 回答
3

关闭最外面的就足够了(即BufferedReader)。阅读BufferedReader 的源码我们可以看到它在Reader调用自己的 close 方法时关闭了内部:

513       public void close() throws IOException {
514           synchronized (lock) {
515               if (in == null)
516                   return;
517               in.close();
518               in = null;
519               cb = null;
520           }
521       }
522   }
于 2012-06-29T14:49:59.577 回答
0

根据经验,您应该按照打开它们的相反顺序关闭所有内容。

于 2012-06-29T14:47:35.900 回答
0

我会按照您打开它们的相反顺序关闭它们,就好像打开它们时会将阅读器推入堆栈,而关闭会将阅读器从堆栈中弹出一样。

最后,全部关闭后,“阅读器堆栈”一定是空的。

于 2012-06-29T14:48:30.497 回答
0

您只需要关闭实际资源。即使构造装饰器失败,您也应该关闭资源。对于输出,您应该在快乐的情况下刷新最多的装饰器对象。

一些并发症:

  • 有时装饰器是不同的资源(一些压缩实现使用 C 堆)。
  • 在悲伤的情况下关闭装饰器实际上会导致刷新,随之而来的是混乱,例如实际上并没有关闭底层资源。
  • 看起来你的底层资源是 a URLConnection,它没有disconnect/close方法。

您可能希望考虑使用 Execute Around 习语,这样您就不必重复这种事情。

于 2012-06-29T14:54:58.690 回答