是否有一种优雅的方法来处理 finally
块中抛出的异常?
例如:
try {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
finally {
try{
resource.close();
}
catch( Exception ex ) {
// Could not close the resource?
}
}
你如何避免 finally
块中的 try
/ catch
?
14 回答
我通常使用
org.apache.commons.io.IOUtils
中的一个closeQuietly
方法:将
Resource
从best answer更改为CloseableStreams实现
Closeable
因此,您可以为所有流重用该方法任务完成 . 没有空测试 . 单一捕获,包括获取和释放异常 . 当然,您可以使用Execute Around习语,只需为每种资源类型编写一次 .
如果您正在使用Java 7,并且
resource
实现AutoClosable
,则可以执行此操作(使用InputStream作为示例):经过多次考虑后,我发现以下代码最佳:
该代码保证以下内容:
代码完成后释放资源
关闭资源时抛出的异常不会在不处理的情况下消耗掉 .
代码不会尝试关闭资源两次,也不会创建不必要的异常 .
我通常这样做:
别处:
忽略'finally'块中发生的异常通常是一个坏主意,除非知道这些异常将是什么以及它们将代表什么条件 . 在正常的
try/finally
使用模式中,try
块将事物置于外部代码不期望的状态,并且finally
块将这些事物的状态恢复为外部代码所期望的状态 . 捕获异常的外部代码通常会期望,尽管有异常,但所有内容都已恢复到normal
状态 . 例如,假设某些代码启动一个事务,然后尝试添加两个记录; "finally"块执行"rollback if not committed"操作 . 调用者可能已准备好在执行第二个"add"操作期间发生异常,并且可能期望如果它捕获到这样的异常,则数据库将处于尝试任一操作之前的状态 . 但是,如果在回滚期间发生第二个异常,则如果调用者对数据库状态做出任何假设,则可能会发生错误 . 回滚失败代表了一个重大危机 - 一个不应该被期望仅仅"Failed to add record"异常的代码捕获的危机 .我个人倾向于有一个finally方法捕获发生的异常并将它们包装在“CleanupFailedException”中,认识到这种失败代表了一个主要问题,并且不应该轻易 grab 这样的异常 .
我经常这样做:
理由:如果我已经完成了资源并且我唯一的问题是关闭它,那么我无能为力 . 如果我已经完成了资源,那么杀死整个线程也没有意义 .
这是至少对我而言,忽略该检查异常是安全的情况之一 .
直到今天,我还没有使用这个成语的任何问题 .
一种解决方案,如果两个异常是两个不同的类
但有时你无法避免这种第二次尝试 . 例如关闭流
你可以将它重构成另一种方法......
如果可以,您应该进行测试以避免错误情况开始 .
此外,您应该只捕获可以恢复的异常,如果无法恢复,则将其传播到程序的顶层 . 如果您无法测试错误情况,则必须使用try catch块来包围代码,就像您已经完成的那样(尽管我建议仍然捕获特定的,预期的错误) .
从Java 7开始,您不再需要显式关闭 finally 块中的资源,而是可以使用 try -with-resources语法 . try-with-resources语句是一个声明一个或多个资源的try语句 . 资源是在程序完成后必须关闭的对象 . try-with-resources语句确保在语句结束时关闭每个资源 . 实现java.lang.AutoCloseable的任何对象(包括实现java.io.Closeable的所有对象)都可以用作资源 .
假设以下代码:
如果发生任何异常,将按照创建它们的相反顺序在这三个资源中的每一个上调用 close 方法 . 这意味着将首先为ResultSetm调用close方法,然后调用Statement,最后调用Connection对象 .
同样重要的是要知道自动调用close方法时发生的任何异常都会被抑制 . 可以通过 Throwable 类中定义的 getsuppressed() 方法检索这些抑制的异常 .
资料来源:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
可以说有点超过顶部,但如果你让异常冒出来并且你无法记录任何内容,那么它可能很有用在你的方法中(例如,因为它是一个库,你宁愿让调用代码处理异常和日志记录):
更新:我对此进行了一些调查,发现了一篇很棒的博客文章,其中有一个人比我更清楚地想到这个:http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html他更进了一步,将两个例外结合成一个,我认为在某些情况下有用 .
你为什么要避免额外的阻止?由于finally块包含可能引发异常的“正常”操作,并且您希望finally块完全运行,因此您必须捕获异常 .
如果你不希望finally块抛出异常并且你不知道如何处理异常(你只是转储堆栈跟踪)让异常冒泡调用堆栈(从最后删除try-catch)块) .
如果你想减少输入,你可以实现一个“全局”外部try-catch块,它将捕获finally块中抛出的所有异常: