为什么抑制try-with-resource的异常以相反的执行顺序处理?

时间:2015-07-17 18:09:46

标签: java exception exception-handling try-with-resources

在正常的try-catch-finally中,像这样,

try {
    throw new ExceptionA();
} finally {
    throw new ExceptionB();
}

ExceptionA在Exception B之前抛出.ExceptionA将被禁止。

但是在try-with-resource中,像这样,

try ( // declare resource that throw ExceptionA in close method ) {
    throw new ExceptionB();
}

在ExceptionB之后抛出ExceptionA。 ExceptionA将被禁止。

为什么他们有不同的抑制异常的命令?

3 个答案:

答案 0 :(得分:5)

当你使用try并最终没有try-with-resources时,当try块中出现错误时,你会抛出异常,然后执行finally,如果在finally块期间抛出异常,那么异常最终掩码抛出 try块抛出的异常。 “异常屏蔽”是JVM选择从try-finally抛出的异常是finally块中的异常,而不是原始异常的位置。这可能非常糟糕,因为try块抛出的异常是带有错误信息的异常,finally抛出的异常通常只是噪声。因此,在上面的示例A中,从实现者的角度来看,所发生的事情是直观的,但它对应用程序开发人员没有用处;关于实际出错的重要信息是在A中你丢失了,而抛出的是B和B的堆栈跟踪是你在阅读日志文件时看到的。

例如:我的代码发出一个JDBC调用,抛出一个带有行号的异常,告诉我错误发生的位置,以及我可以映射回供应商代码的SQLState,告诉我出了什么问题,但是当关闭语句时或者连接存在网络故障,因为JDBC对象正在告诉服务器服务器应该清理什么,并且我得到了一个断管异常。除非你对异常处理非常彻底,否则很容易用破坏的管道掩盖有用的异常,你不能做任何事情而不关心。

try-with-resources功能尝试确保信息性异常不会被关闭时抛出的偶然异常所掩盖,try块中抛出的异常是被抛出的异常,而close方法抛出的异常是out out被抑制,这意味着它被添加到try块的异常中(除非try块中没有抛出任何内容,在这种情况下,close抛出的异常就是被抛出的内容)。

所以这是一个改变,但它在减少有价值的异常被无意中掩盖的可能性方面有了很大的改进。

答案 1 :(得分:2)

C ++异常处理范例的一个弱点是,它主要由Java继承而后由.NET继承,它无法有效处理发生异常的情况,需要清理(堆栈展开),以及展开的过程第一个异常的堆栈会触发第二个异常。在C ++中,第二个异常将基本上杀死所有内容。 Java和.NET的设计者显然不喜欢这种行为,但两种框架都没有任何方法可以同时解除两个异常。 Java的设计者认为,当引发第二个异常时,系统放弃第一个异常(以及任何从中展开堆栈的尝试)的最小恶意行为,但即使它是"最少的邪恶"它仍然很邪恶。 .NET的实现者遵循Java模式。

要真正妥善处理这种情况,需要提供清理代码,其中包含有关是否正在运行以响应异常的信息,如果是,那么它是什么。如果清理代码中出现异常情况,这样的信息将使清理代码能够确保如果它失败,它只会替换先前的例外,如果它更重要,并且还要确保早期的证据。异常在任何情况下都会被保留。不幸的是,除了catchtry块中的代码重复之外,没有标准惯例让清理代码知道它的运行原因。

"尝试使用资源"构造假定首先发生的异常更可能对调用代码很重要,尽管它保留了清理时发生的异常的证据。这并不等于允许清理代码确定其异常是否比原始异常更重要或更不重要,但它比清除代码破坏所有先前异常的证据或避免抛出更好。完全例外,因为它无法在不扼杀其他例外情况的情况下告诉它何时能够这样做。

答案 2 :(得分:0)

这是因为在try-with-resources语句中,所有资源在 try块之后立即关闭,即使try块抛出异常

try (
    // resource a which throws A when closed
) {
    // exception B thrown from here
} // resource a closed HERE

这意味着B抛出第一个A 第二个是完全合乎逻辑的。

因此A将被“{抑制”(附加到)B而不是相反。

当然,如果你可以首先打开资源a,这是正确的;如果打开它会引发异常,B将无法被抛出......