36

好吧,我一直在做以下事情(变量名已更改):


FileInputStream fis = null;
try
{
    fis = new FileInputStream(file);

    ... process ...

}
catch (IOException e)
{
    ... handle error ...
}
finally
{
    if (fis != null)
        fis.close();
}

最近,我开始使用 FindBugs,这表明我没有正确关闭流。我决定看看 finally{} 块是否可以做任何事情,然后我看到,哦,是的,close() 可以抛出 IOException。人们应该在这里做什么?Java 库抛出了太多的检查异常。

4

9 回答 9

47

对于 Java 7 及更高版本,应使用try-with-resources :

try (InputStream in = new FileInputStream(file)) {
  // TODO: work
} catch (IOException e) {
  // TODO: handle error
}

如果您卡在 Java 6 或更低版本...

这种模式避免了使用null

    try {
        InputStream in = new FileInputStream(file);
        try {
            // TODO: work
        } finally {
            in.close();
        }
    } catch (IOException e) {
        // TODO: error handling
    }

有关如何有效处理close的更多详细信息,请阅读此博客文章:Java:如何不弄乱流处理。它有更多的示例代码、更多的深度,并涵盖了在catch块中包裹关闭的陷阱。

于 2008-10-01T09:44:16.553 回答
26

像下面这样的事情应该做到这一点,取决于你是否在尝试关闭流时抛出或吞下 IOException。

FileInputStream fis = null;
try
{
    fis = new FileInputStream(file);

    ... process ...


}
catch (IOException e)
{
    ... blah blah blah ...
}
finally
{
    try
    {
        if (fis != null)
            fis.close();
    }
    catch (IOException e)
    {
    }
}
于 2008-10-01T07:05:57.123 回答
10

您可以使用添加了 JDK7 的try-with-resources功能。正是为了处理这种事情而创建的

static String readFirstLineFromFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}

文档说:

try-with-resources 语句确保每个资源在语句结束时关闭。

于 2012-05-17T22:35:30.993 回答
4

您还可以使用简单的静态辅助方法:

public static void closeQuietly(InputStream s) {
   if (null == s) {
      return;
   }
   try {
      s.close();
   } catch (IOException ioe) {
      //ignore exception
   }
}

并从您的 finally 块中使用它。

于 2008-10-01T07:12:03.550 回答
3

没什么可补充的,除了一个非常小的风格建议。 自我记录代码的规范示例IOException适用于这种情况 - 为您必须了解的被忽略的变量提供描述性变量名称close()

所以 squidle 的回答变成了:

public static void closeQuietly(InputStream s) {
   try {
      s.close();
   } catch (IOException ignored) {
   }
}
于 2008-10-01T08:12:45.637 回答
2

在大多数情况下,我发现最好不要捕获 IO 异常,而只需使用 try-finally:

final InputStream is = ... // (assuming some construction that can't return null)
try {
    // process is
    ...
} finally {
    is.close();
}

除了FileNotFoundException,您通常无法“解决” IOException. 剩下要做的唯一一件事就是报告一个错误,您通常会在调用堆栈中进一步处理它,所以我发现传播异常更好。

由于IOException是已检查异常,因此您必须声明此代码(及其任何客户端)throws IOException。这可能太吵了,或者您可能不想透露使用 IO 的实现细节。在这种情况下,您可以使用一个异常处理程序来包装整个块,该异常处理程序包含IOException一个RuntimeException或一个抽象异常类型。

详细信息:我知道try当块中的close操作finally产生IOException. 我不认为这是一个大问题:通常,块中的异常与导致失败的异常try相同(即 IO 正常工作然后在关闭时失败的情况非常罕见)。如果这是一个问题,那么“沉默”收盘可能是值得的。IOExceptionclose

于 2008-10-01T10:52:02.243 回答
1

如果关闭失败,以下解决方案会正确抛出异常,而不会在关闭之前隐藏可能的异常。

try {
    InputStream in = new FileInputStream(file);
    try {
        // work
        in.close();
    } finally {
        Closeables.closeQuietly(in);
    }
} catch(IOException exc) {
    // kernel panic
}

这是有效的,因为第二次调用 close没有效果

这依赖于 guava Closeables,但如果愿意,可以编写自己的 closeQuietly 方法,如squidle所示(另见serg10)。

在一般情况下,报告关闭错误很重要,因为关闭可能会将一些最终字节写入流,例如由于缓冲。因此,您的用户想知道它是否失败,或者您可能想以某种方式采取行动。当然,在 FileInputStream 的特定情况下这可能不是真的,我不知道(但由于已经提到的原因,我认为如果它仍然发生,最好报告一个关闭错误)。

由于嵌入的 try 块的结构,上面的代码有点难以掌握。使用两种方法可能会更清晰,一种抛出 IOException,另一种捕获它。至少这是我会选择的。

private void work() throws IOException {
    InputStream in = new FileInputStream(file);
    try {
        // work
        in.close();
    } finally {
        Closeables.closeQuietly(in);
    }
}

public void workAndDealWithException() {
    try {
        work();
    } catch(IOException exc) {
        // kernel panic
    }
}

基于http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html(由 McDowell 引用)。

于 2011-07-23T19:28:56.283 回答
0

希望有一天我们会在 Java 中获得闭包,然后我们将减少很多冗长的内容。

因此,您可以导入 javaIO 中的某个辅助方法,它可能需要一个“可关闭”接口和一个块。在该辅助方法中,try {closable.close() } catch (IOException ex){ //blah} 被一劳永逸地定义,然后您就可以编写

 Inputstream s = ....;
 withClosable(s) {
    //your code here
 }
于 2008-10-01T07:18:51.070 回答
-4

您主要关心的是从 FindBugs 获得一份干净的报告还是让代码有效?这些不一定是同一件事。您的原始代码很好(尽管我会摆脱冗余if (fis != null)检查,OutOfMemoryException否则会抛出 an )。FileInputStream 有一个终结器方法,它会在您在处理过程中实际收到 IOException 的不太可能的情况下为您关闭流。为了避免极不可能的情况而使您的代码更复杂,这根本不值得

  1. 你得到一个 IOException 和
  2. 这种情况经常发生,以至于您开始遇到终结器积压问题。

编辑:如果您收到如此多的 IOExceptions 以至于您遇到了终结器队列的问题,那么您有更大的鱼要炸!这是关于获得透视感。

于 2008-10-01T08:37:48.117 回答