在Java中关闭输入流

Closing inputstreams in Java

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

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

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


按照约定,包装器流(用于包装现有流)在关闭时会关闭基础流,因此在您的示例中只需关闭bufferedreader。而且,关闭已经关闭的流通常是无害的,因此关闭所有3个流不会受到伤害。


通常,只关闭最外部的流是可以的,因为按照惯例,它必须触发基础流的关闭。

因此,通常代码如下所示:

1
2
3
4
5
6
7
8
9
10
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为null。因此,finally块不会关闭任何内容,从而使基础流保持打开状态。

从Java 7开始,您可以执行以下操作:

1
2
3
4
5
    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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    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的模式容易出错的情况下,我可以想到的每个用例中,我的模式都是白痴安全的。


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

1
2
3
4
5
6
7
8
9
10
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   }

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

一些并发症:

  • 有时,装饰器是不同的资源(某些压缩实现使用C堆)。
  • 在糟糕的情况下关闭装饰器实际上会导致刷新,并产生混乱,例如实际上并未关闭基础资源。
  • 看起来您的基础资源是URLConnection,它没有这样的disconnect / close方法。

您可能希望考虑使用Execute Around惯用语,因此您不必重复这种事情。


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

最后,在全部关闭之后,"阅读器堆栈"必须为空。


根据经验,应按打开它们的相反顺序关闭所有组件。