在Java中关闭inputstreams

在Java中关闭inputstreams,java,inputstream,Java,Inputstream,我在try/catch块中有以下代码 InputStream inputstream = conn.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); 我的问题是,当我必须在finally块中关闭这些流时,我是否必须

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

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

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

按照惯例,包装流(包装现有流)在关闭时关闭基础流,因此,在您的示例中,只需关闭
bufferedreader
。此外,关闭一个已经关闭的流通常是无害的,因此关闭所有3个流不会有任何伤害。

根据经验法则,您应该按与打开顺序相反的顺序关闭所有流。

我会按与打开顺序相反的顺序关闭所有流,好像打开它们会将读卡器推到堆栈中,关闭它们会将读卡器从堆栈中弹出


最后,在全部关闭后,“读卡器堆栈”必须为空。

关闭最外面的一个就足够了(即
BufferedReader
)。读取时,我们可以看到,当调用其自己的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   }

您只需要关闭实际的资源。即使构建装饰程序失败,也应该关闭资源。对于输出,您应该刷新最适合的decorator对象

一些并发症:

  • 有时装饰器是不同的资源(一些压缩实现使用C堆)
  • 在sad情况下关闭decorator实际上会导致刷新,从而导致混乱,例如没有实际关闭底层资源
  • 看起来您的底层资源是
    URLConnection
    ,它本身没有
    断开连接
    /
    关闭
    方法

您可能希望考虑使用围绕习语的执行,这样您就不必重复这种事情。

正常地关闭最外层流是正常的,因为按照惯例它必须触发底层流的关闭。 所以通常代码如下所示:

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
}
然而,在很少的情况下,底层流构造函数会在流已经打开的情况下引发异常。在这种情况下,上述代码不会关闭底层流,因为从未调用外部构造函数,并且中的
为null。因此,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()调用

编辑 以下是一些非信徒的代码,其中使用此模式非常重要。您可能还想阅读ApacheCommons IOUtils javadoc中关于方法的内容


当创建
out2
引发异常时,使用@Tom的“建议”将使
out1
保持打开状态。这条建议来自于一个谈论
的人,因为显而易见的原因,这是一个持续不断的错误源。
好吧,我可能是瞎子,但这对我来说并不明显。我的模式在我能想到的每一个用例中都是白痴安全的,而Tom的模式很容易出错。

如果流实现已经关闭,则不能保证它在关闭时不会抛出异常。因此,最好坚持正确的方法,只关闭BufferedReader@Soronthar那么这个流实现根据契约是无效的,契约声明
如果流已经关闭,那么调用这个方法就没有效果。
所有有效的实现都必须这样做。关闭已经关闭的流是完全合法的(也是常见的)。通常,您第一次在try块中关闭它,第二次在finally块中关闭它,在finally块中关闭try块使您有机会对异常作出反应,而finally块中关闭是为了确保关闭。@FabianBarney您是对的,我忘了他们在JSE 6中用Closeable接口改装了流。这不仅仅是按照惯例,它是指定的。例如,请参见FilerInputStream.close()。请不要做那种
null
舞蹈。由于显而易见的原因,这是一个持续不断的错误来源。真是一团糟。这是正常的模式。您还想做什么?
acquire();试试{use();}最后{release();}
@Bruno Right-这就是我在最后一段中的意思。@TomHawtin-tackline,这样当acquire引发异常时您就不会真正放松了?这是不好的,特别是当使用像TeeOutputStream这样的特殊OutputStream时,其中一个已经创建,而另一个失败了。您提出这一建议的原因是什么?当指定过滤输入流和读卡器关闭close()上的嵌套流时?我想这只是常识。正如投票所显示的那样,在这个问题上有更好的答案。有些人甚至展示了库的源代码
    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);
    }