获取/关闭资源时,Java try / catch / finally最佳实践 [英] Java try/catch/finally best practices while acquiring/closing resources

查看:626
本文介绍了获取/关闭资源时,Java try / catch / finally最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在开展学校项目时,我编写了以下代码:

While working on a school project, I wrote the following code:

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告诉我资源.close()行抛出 IOException ,因此必须被捕获或声明。它还抱怨 oos fos 可能尚未初始化(尽管检查为空)。

The problem is that Netbeans is telling me the resource.close() lines throw an IOException and therefore must either be caught or declared. It also is complaining that oos and fos might not yet be initialized (despite the null checks).

这看起来有点奇怪,看看整个点是如何在那里停止 IOException

This seems a little strange, seeing as how the whole point is to stop the IOException right there.

我的下意识修复是这样做的:

My knee-jerk fix is to do this:

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

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

But deep down this bothers me and feels dirty.

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

I come from a C# background, where I would simply take advantage of a using block, so I am unsure of what the "right" way is to handle this.

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

What is the right way to handle this problem?

推荐答案

如果您尝试在源头捕获并报告所有异常,可以采用更好的解决方案:

If you are trying to catch and report all exceptions at source, a better solution is this:

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包装器流,读者和编写器都传播关闭刷新到它们的包装流等。所以你只需要关闭或刷新最外面的包装器。

  • 在try块结束时显式刷新的目的是这样的 IOException 的(实际)处理程序可以看到任何写入失败 1

  • 当你在输出流上关闭或刷新,有一个一个月亮一次的机会,由于光盘错误或文件系统已满而引发异常。 你不应该压缩这个例外!

  • The standard Java wrapper streams, readers and writers all propagate close and flush to their wrapped streams, etc. So you only need to close or flush the outermost wrapper.
  • The purpose of flushing explicitly at the end of the try block is so that the (real) handler for IOException gets to see any write failures1.
  • When you do a close or flush on an output stream, there is a "once in a blue moon" chance that an exception will be thrown due to disc errors or file system full. You should not squash this exception!.

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

If you often have to "close a possibly null stream ignoring IOExceptions", then you could write yourself a helper method like this:

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

然后你可以用以下代码替换之前的finally块:

then you can replace the previous finally block with:

} finally {
    closeQuietly(oos);
}

(另一个答案指出 closeQuietly 方法已在Apache Commons库中提供...如果您不介意为10行方法添加项目依赖项。更新:请注意,这些方法已弃用在API的2.6版本中。)

(Another answer points out that a closeQuietly method is already available in an Apache Commons library ... if you don't mind adding a dependency to your project for a 10 line method. UPDATE : note that these methods are deprecated in version 2.6 of the API.)

但是请注意,在IO异常的流中只使用 closeQuietly em>真的无关紧要。

But be careful that you only use closeQuietly on streams where IO exceptions really are irrelevant.

1 - 使用try-with-resources时没有必要。

关于 flush()关闭的问题关闭()人们在询问:


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

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

  • 最后,问题是 flush()实际上是什么。 javadoc说的是这个(对于 OutputStream ...)

  • The standard "filter" and "buffered" output streams and writers have an API contract that states that close() causes all buffered output to be flushed. You should find that all other (standard) output classes that do output buffering will behave the same way. So, for a standard class it is redundant to call flush() immediately before close().
  • For custom and 3rd-party classes, you need to investigate (e.g. read the javadoc, look at the code), but any close() method that doesn't flush buffered data is arguably broken.
  • Finally, there is the issue of what flush() actually does. What the javadoc says is this (for 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 方法。 ..)

So ... if you hope / imagine that calling flush() guarantees that your data will persist, you are wrong! (If you need to do that kind of thing, look at the FileChannel.force method ...)

另一方面,如果你可以使用Java 7或更高版本,@ Michael Clark的答案中描述的新尝试资源是最佳解决方案。

On the other hand, if you can use Java 7 or later, the "new" try-with-resources as described in @Mike Clark's answer is the best solution.

如果您的新代码没有使用Java 7或更高版本,那么您可能会陷入困境并深入挖掘。

If you are not using Java 7 or later for your new code, you are probably in a hole and digging deeper.

这篇关于获取/关闭资源时,Java try / catch / finally最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆