使用Apache commons-io IOUtils.closeQuietly安全吗?

41

这段代码是否正确?

    BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
    try {
        bw.write("test");
    } finally {
        IOUtils.closeQuietly(bw);
    }

安全还是不安全?据我所了解,当我们关闭 BufferedWriter 时,它会将缓冲区刷新到底层流并可能由于错误而失败。但是 IOUtils.closeQuietly API 表示任何异常都将被忽略。

由于 IOUtils.closeQuietly,可能存在未被注意到的数据丢失吗?


9
使用“try-with-resources”语句可以消除大多数使用IOUtils.closeQuietly的需要。参见:http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html - lyomi
1
很好的答案,但这个特性是Java 7中的新功能。如果你因为某些原因(例如旧版Android)而被困在使用Java 6上,那么closeQuietly()仍然是最好的选择。 - Edward Falk
IOUtils.closeQuietly已被弃用,现在请使用try with resource。 - best wishes
如果资源的close()方法抛出一个已检查异常,那么仍然需要使用closeQuietly(..)。在这种情况下,无法使用try-with-resources,因为close()上的异常将会冒泡。 - Sven Döring
4个回答

45

根据closeQuietly()的javadoc,代码应该像这样:

BufferedWriter bw = null;

try {
    bw = new BufferedWriter(new FileWriter("test.txt"));
    bw.write("test");
    bw.flush(); // you can omit this if you don't care about errors while flushing
    bw.close(); // you can omit this if you don't care about errors while closing
} catch (IOException e) {
    // error handling (e.g. on flushing)
} finally {
    IOUtils.closeQuietly(bw);
}

closeQuietly()并非用于一般用途,而是用来替代在可关闭对象上直接调用close()。它的使用场景是确保在finally块内正确关闭 - 所有所需的错误处理都必须在其之前完成。

这意味着,如果您想在调用close()flush()期间对异常进行反应,则必须按照正常方式处理它。在finally块中添加closeQuietly()只能确保关闭操作,例如当flush操作失败时,在try块中没有调用close操作。


我认为bw.flush()是不必要的,因为bw.close()会自动调用flush()。 - Evgeniy Dorofeev
2
@EvgeniyDorofeev 是的,只要你在那里保留 close()。但是当你只关心 flush 而不是 close 期间的异常时,你需要在这里显式地执行 flush。刚刚写了两个方法调用,使示例更通用一些。 - Fabian Barney
只是为了明确:您可以省略对bw.close()的调用,而bw仍将在finally{}子句中正确关闭。以这种方式执行还允许您在bw.close()期间了解任何错误。 - Edward Falk
1
close() 不总是调用 flush(),有时 close() 调用 flush 但在其中掩盖了 IOException。例如,请参见 FilterOutputStream - Gray

8
只要您的应用程序不关心写入是否成功,它就是安全的。如果您的应用程序需要处理写入错误,则不安全,因为关闭时缓冲数据被刷新可能会丢失并且错误会被忽略。

6

使用IOUtils.closeQuietly是安全的,但仅适用于Java6及以下版本。从Java7开始,您应该使用try-with-resource

这将消除大部分样板代码和使用IOUtils.closeQuietly的需求。

现在,您可以参考下面的示例:

    BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
    try {
        bw.write("test");
    } finally {
        IOUtils.closeQuietly(bw);
    }

可以写成:

   try (BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"))) {
       bw.write("test");
   }

需要注意的是,在使用try-with-resource方法时,您的资源需要实现一个新的接口,称为java.lang.AutoCloseable,该接口在Java 7中引入。

另外,您可以在try-with-resource块中包含多个资源,只需用“;”分隔即可。

   try (
       BufferedWriter bw1 = new BufferedWriter(new FileWriter("test1.txt"));
       BufferedWriter bw2 = new BufferedWriter(new FileWriter("test2.txt"))
   ) {
       // Do something useful with those 2 buffers!
   }   // bw1 and bw2 will be closed in any case

5
理论上来说是有可能的,但我从未见过close()方法失败的情况。通常所谓的“快速失败”意味着先前的I/O操作(如打开文件)将首先失败。你可以编写一个不忽略IOException异常的close()方法,但如果它在try/catch块中发生了异常,这样做可能会掩盖真正的异常原因。你需要的是像下面这样的代码(大多数情况下这种方式已经足够用了)。
try {
    // write to bw.
    bw.close(); // throw IOException if an error occurs.

} finally {
    // don't clobber a previous IOException
    IOUtils.closeQuietly(bw);
}

2
您正在错误地使用它 - IOUtils.closeQuietly() 不是用于一般用途的,而是应该直接调用可关闭对象的 close() 方法。它的预期用例是确保在 finally 块中调用 close() 以避免资源泄漏 - 所有错误处理(日志记录、异常包装和重新抛出)都应在此之前完成 - 请参见 Fabian 的答案。 - Jarek Przygódzki
@JarekPrzygódzki 你上次特别遇到IOException是什么时候? - Peter Lawrey
这种情况虽然不常见,但确实会发生 - 你永远不知道Closeable或{Input, Output}Stream后面隐藏了什么。同时,忽略异常几乎从来都不是一个好主意。 - Jarek Przygódzki
2
我已经检查了源代码并阅读了文档。closeQuietly()仅仅是一个调用close()的try/catch块,忽略异常。文档也是这样说明的。你可以使用closeQuietly()代替close(),而finally{ }块是理想的位置。 - Edward Falk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接