Java 为什么try with resource的受抑制异常按与执行相反的顺序处理?

Java 为什么try with resource的受抑制异常按与执行相反的顺序处理?,java,exception,exception-handling,try-with-resources,Java,Exception,Exception Handling,Try With Resources,在正常情况下,最后试捕,就像这样 try { throw new ExceptionA(); } finally { throw new ExceptionB(); } try ( // declare resource that throw ExceptionA in close method ) { throw new ExceptionB(); } ExceptionA在异常B之前抛出。ExceptionA将被抑制 但在尝试使用资源时,就像这样 try {

在正常情况下,最后试捕,就像这样

try {
    throw new ExceptionA();
} finally {
    throw new ExceptionB();
}
try ( // declare resource that throw ExceptionA in close method ) {
    throw new ExceptionB();
}
ExceptionA在异常B之前抛出。ExceptionA将被抑制

但在尝试使用资源时,就像这样

try {
    throw new ExceptionA();
} finally {
    throw new ExceptionB();
}
try ( // declare resource that throw ExceptionA in close method ) {
    throw new ExceptionB();
}
ExceptionA在ExceptionB之后抛出。例外A将被禁止


为什么它们有不同的抑制异常的顺序?

这是因为在
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
抑制,而不是相反



当然,如果您首先可以打开resource
a
,这是正确的;如果打开时抛出异常,
B
将根本没有被抛出的机会…

当您使用try和finally而不使用try-with-resources时,当try块出错时,抛出异常,然后执行finally,如果在finally块期间抛出异常,那么finally抛出的异常会掩盖try块抛出的异常。“异常屏蔽”是JVM选择从try finally抛出的异常是来自finally块的异常,而不是原始异常。这可能非常糟糕,因为try块抛出的异常包含出错的信息,finally块抛出的异常通常只是噪声。因此,在上面的示例中,除了一个例外,从实现者的角度来看,所发生的事情是直观的,但对应用程序开发人员没有用处;关于实际出错的有价值的信息在A中,您会丢失它,而抛出的是B,B的stacktrace是您在读取日志文件时看到的

例如:我的代码发出一个JDBC调用,抛出一个异常,一个行号告诉我错误发生在哪里,一个SQLState我可以映射回一个供应商代码告诉我哪里出了问题,但是当关闭语句或连接时,出现了一个网络故障,因为JDBC对象告诉服务器服务器应该清理什么,我得到了一个断管异常。除非您对异常处理非常彻底,否则很容易用管道破裂的异常来掩盖有用的异常,而管道破裂的异常是您无法处理且不关心的

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


<>这是一个改变,但是在减少有价值的异常的机会时,会被大大地掩盖。

一个C++异常处理范例的弱点,它主要是由java继承,又是.net所造成的,它不能有效地处理异常,需要清除的情况发生。(堆栈展开)第二个例外基本上会杀死所有的东西。java和.net的设计者显然不喜欢这个行为,但是两个框架都没有同时解开两个异常的方法。java设计者决定了EVI的最小C++。ls行为是在抛出第二个异常时让系统放弃第一个异常(以及从中释放堆栈的任何尝试),但即使它是“最小的邪恶”,它仍然是相当邪恶的。NET的实现者遵循Java模式

要真正干净地处理这种情况,需要向清理代码提供有关它是否在响应异常时运行以及(如果是)它是什么的信息。如果清理代码中出现异常情况,这些信息将使清理代码能够确保,如果它失败,它将只替换如果早期异常更重要,还应确保在任何情况下都保留早期异常的证据。不幸的是,除了在
catch
try
块中复制代码之外,没有标准约定让清理代码知道为什么要运行它


“尝试资源”构造假定首先发生的异常对调用代码更为重要,尽管它保留了清理时发生的异常的证据。这不如让清理代码确定其异常是否比原始异常更重要,但这比让清理代码本身更重要要好er销毁所有早期异常的证据,或者完全避免抛出异常,因为它无法判断何时可以这样做而不扼杀其他异常。

很抱歉,我没有说清楚。在第二个示例中,我的意思是从close方法抛出异常。是的,但不是。请看我的答案。事实上try块中抛出的异常不会阻止资源关闭!哦,我明白你的意思。就像@Nathan说的,自然顺序应该是早期异常抑制后期异常。那么为什么在try finally示例中,异常以相反的顺序抑制?是设计不好还是有其他原因?可能是因为它是guaRanted表示将执行finally块;由于在本例中,
finally
抛出异常,它将从之前的任何代码中收集异常。至少这是我的猜测。那么,您的意思是说try final示例中不自然的抑制顺序是错误的设计吗