Java try/catch/finally 获取/关闭资源时的最佳实践

标签 java resources try-catch

在做一个学校项目时,我编写了以下代码:

FileOutputStream fos;
ObjectOutputStream oos;
try {
    fos = new FileOutputStream(file);
    oos = new ObjectOutputStream(fos);

    oos.writeObject(shapes);
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) oos.close();
    if (fos != null) fos.close();
}

问题是 Netbeans 告诉我 resource.close() 行抛出 IOException 因此必须要么被捕获要么声明。它还提示 oosfos 可能尚未初始化(尽管进行了空检查)。

这似乎有点奇怪,关键是如何将 IOException 停在那里。

我的下意识的解决办法是这样做:

} finally {
    try {
        if (oos != null) oos.close();
        if (fos != null) fos.close();
    } catch (IOException ex) { }
}

但在内心深处,这让我很困扰,感觉很脏。

我来自 C# 背景,我只是利用 using block ,所以我不确定处理这个问题的“正确”方法是什么。

什么是处理这个问题的正确方法?

最佳答案

请注意,以下内容仅适用于 Java 6 及更早版本。对于 Java 7 及更高版本,您应该切换到使用 try-with-resources ...,如其他答案中所述。

如果您试图从源头(Java 6 或更早版本)捕获和报告所有异常,更好的解决方案是:

ObjectOutputStream oos = null;
try {
   oos = new ObjectOutputStream(new FileOutputStream(file));
   oos.writeObject(shapes);
   oos.flush();
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) {
        try {
            oos.close();
        } catch (IOException ex) {
            // ignore ... any significant errors should already have been
            // reported via an IOException from the final flush.
        }
    }
}

注意事项:

  • 标准的 Java 包装流、读取器和写入器都将 closeflush 传播到其包装的流等。因此您只需要关闭或刷新最外层的包装器。
  • 在 try block 末尾显式刷新的目的是为了让 IOException 的(实际)处理程序能够看到任何写入失败1
  • 当您对输出流执行关闭或刷新时,由于磁盘错误或文件系统已满而引发异常的可能性“千载难逢”。 你不应该压制这个异常(exception)!

如果您经常需要“关闭一个可能为空的流而忽略 IOExceptions”,那么您可以为自己编写一个这样的辅助方法:

public void closeQuietly(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ex) {
            // ignore
        }
    }
}

那么你可以将前面的 finally block 替换为:

} finally {
    closeQuietly(oos);
}

另一个答案指出 closeQuietly 方法已经在 Apache Commons 库中可用...如果您不介意为项目添加 10 行方法的依赖项。

但请注意,您仅在 IO 异常真正无关紧要的流上使用 closeQuietly

UPDATE:closeQuietly 在 Apache Commons API 2.6 版中已弃用。 Java 7+ try-with-resources 让它变得多余。


关于人们在评论中询问的 flush()close() 的问题:

  • 标准的“过滤器”和“缓冲”输出流和写入器有一个 API 协定,规定 close() 会导致所有缓冲输出被刷新。您应该发现执行输出缓冲的所有其他(标准)输出类的行为方式相同。因此,对于标准类,在 close() 之前立即调用 flush() 是多余的。

  • 对于自定义类和第 3 方类,您需要调查(例如阅读 javadoc,查看代码),但任何不刷新缓冲数据的 close() 方法可以说是坏了

  • 最后,flush() 究竟做了什么。 javadoc 说的是这个(对于 OutputStream ...)

    If the intended destination of this stream is an abstraction provided by the underlying operating system, for example a file, then flushing the stream guarantees only that bytes previously written to the stream are passed to the operating system for writing; it does not guarantee that they are actually written to a physical device such as a disk drive.

    所以...如果你希望/想象调用 flush() 可以保证你的数据会持续存在,你错了!(如果你需要这样做事情,看看 FileChannel.force 方法...)

关于Java try/catch/finally 获取/关闭资源时的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4092914/

相关文章:

java - 处理多个窗口中的事件

html - URL 包含原始换行符的资源请求已弃用

ios - XCode如何确定什么是资源,什么不是资源?

javascript - 使用 Promise.catch() 和在 try...catch 中包装 Promise 有什么区别?

java - 为什么会选择使用 finally 语句而不是 catch 语句? ( java )

java - 我的 android 应用程序中有多个 http 请求。线程

java - 方法声明无效;返回类型需要第 10 + 14 行修复后的附加错误

java - 在java中将值插入到json数组的特定位置?

php - include() 资源(css,js)好还是让浏览器再做一个请求好?

java - Java7之后把ResultSet放到一个嵌套的try-with-resources语句中是个好习惯吗?