【问题标题】:Closing inputstreams in Java在 Java 中关闭输入流
【发布时间】:2012-07-01 02:40:30
【问题描述】:

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

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

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

【问题讨论】:

标签: java inputstream


【解决方案1】:

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

所以通常代码是这样的:

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);
    }

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

【讨论】:

  • 请不要跳那种null 的舞蹈。由于显而易见的原因,它是一个持续的错误来源。真是一团糟。
  • 这是正常模式。你还想做什么?
  • acquire(); try { use(); } finally { release(); }
  • @Bruno Right - 这就是我在最后一段中的意思。
  • @TomHawtin-tackline 所以当获取引发异常时你不知道吗?这很糟糕,尤其是在使用像 TeeOutputStream 这样的特殊输出流时,其中一个已经进行创建而另一个失败了。
【解决方案2】:

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

一些并发症:

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

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

【讨论】:

    【解决方案3】:

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

    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   }
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 您提出此建议的原因是什么?当过滤的输入流和阅读器被指定在 close() 时关闭嵌套流?
      • 我猜只是常识。如投票所示,此线程中有更好的答案。有些人甚至展示了该库的源代码。
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 如果流实现已经关闭,你不能保证它在关闭时不会抛出异常。所以最好坚持正确的方式并只关闭 BufferedReader
        • @Soronthar 那么这个流实现在合同中是无效的,合同规定If the stream is already closed then invoking this method has no effect. 所有有效的实现必须像这样。关闭一个已经关闭的流是完全合法的(也是常见的)。通常,您第一次在 try-block 中关闭它,第二次在 finally 块中关闭,在 try-block 中关闭让您有机会对异常做出反应,而在 finally 块中关闭是为了确保关闭。
        • @FabianBarney 你说得对,我忘了他们在 JSE 6 中使用 Closeable 接口改造了流。
        • 这不仅仅是按照惯例,它是指定的。参见例如 FilerInputStream.close()。
        猜你喜欢
        • 2021-03-09
        • 1970-01-01
        • 2015-06-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-05
        相关资源
        最近更新 更多