gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 20:44:45 26 4
gpt4 key购买 nike

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

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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com