在做一个学校项目时,我编写了以下代码:
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
因此必须要么被捕获要么声明。它还提示 oos
和 fos
可能尚未初始化(尽管进行了空检查)。
这似乎有点奇怪,关键是如何将 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 包装流、读取器和写入器都将
close
和flush
传播到其包装的流等。因此您只需要关闭或刷新最外层的包装器。 - 在 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/