java 在进入 finally 块之前是否可以检测异常是否发生?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/184704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to detect if an exception occurred before I entered a finally block?
提问by James Schek
In Java, is there an elegant way to detect if an exception occurred prior to running the finally block? When dealing with "close()" statements, it's common to need exception handling within the finally block. Ideally, we'd want to maintain both exceptions and propagate them up (as both of them may contain useful information). The only way I can think of to do this is to have a variable outside the try-catch-finally scope to save a reference to a thrown exception. Then propagate the "saved" exception up with any that occur in the finally block.
在 Java 中,有没有一种优雅的方法来检测在运行 finally 块之前是否发生了异常?在处理“close()”语句时,通常需要在 finally 块中进行异常处理。理想情况下,我们希望维护两个异常并将它们向上传播(因为它们都可能包含有用的信息)。我能想到的唯一方法是在 try-catch-finally 范围之外使用一个变量来保存对抛出的异常的引用。然后将“保存的”异常与在 finally 块中发生的任何异常一起传播。
Is there a more elegant way of doing this? Perhaps an API call that will reveal this?
有没有更优雅的方法来做到这一点?也许 API 调用会揭示这一点?
Here's some rough code of what I'm talking about:
这是我正在谈论的一些粗略代码:
Throwable t = null;
try {
stream.write(buffer);
} catch(IOException e) {
t = e; //Need to save this exception for finally
throw e;
} finally {
try {
stream.close(); //may throw exception
} catch(IOException e) {
//Is there something better than saving the exception from the exception block?
if(t!=null) {
//propagate the read exception as the "cause"--not great, but you see what I mean.
throw new IOException("Could not close in finally block: " + e.getMessage(),t);
} else {
throw e; //just pass it up
}
}//end close
}
Obviously, there are a number of other similar kludges that might involve saving the exception as an member variable, returning it from a method, etc... but I'm looking for something a bit more elegant.
显然,还有许多其他类似的 kludges 可能涉及将异常保存为成员变量,从方法中返回它,等等......但我正在寻找更优雅的东西。
Maybe something like Thread.getPendingException()or something similar? For that matter, is there an elegant solution in other languages?
也许类似Thread.getPendingException()或类似的东西?就此而言,是否有其他语言的优雅解决方案?
This question actually spawned from comments in another questionthat raised an interesting question.
这个问题实际上是从另一个问题的评论中产生的,这个问题提出了一个有趣的问题。
采纳答案by Jeremy Ross
Your idea about setting a variable outside the scope of the try/catch/finally is correct.
您关于在 try/catch/finally 范围之外设置变量的想法是正确的。
There cannot be more than one exception propagating at once.
一次传播的异常不能超过一个。
回答by Triynko
Instead of using a Boolean flag, I would store a reference to the Exception object. That way, you not only have a way to check whether an exception occurred (the object will be null if no exception occurred), but you'll also have access to the exception object itself in your finally block if an exception did occur. You just have to remember to set the error object in all your catch blocks (iff rethrowing the error).
我不使用布尔标志,而是存储对 Exception 对象的引用。这样,您不仅可以检查是否发生了异常(如果没有发生异常,则对象将为 null),而且如果确实发生了异常,您还可以在 finally 块中访问异常对象本身。您只需要记住在所有 catch 块中设置错误对象(如果重新抛出错误)。
I think this is a missing C# language feature that should be added.The finally block should support a reference to the base Exception class similar to how the catch block supports it, so that a reference to the propagating exception is available to the finally block. This would be an easy task for the compiler, saving us the workof manuallycreating a local Exception variable and rememberingto manually set its value before re-throwing an error, as well as preventing us from making the mistakeof setting the Exception variable when not re-throwing an error (remember, it's only the uncaught exceptions we want to make visible to the finally block).
我认为这是应该添加的缺少的 C# 语言功能。finally 块应该支持对基类 Exception 类的引用,类似于 catch 块支持它的方式,以便对传播异常的引用可用于 finally 块。这将是对编译器的一项容易的任务,节省了我们工作的手动创建本地异常变量和记忆之前,需要手动将其值设置再抛出一个错误,以及阻止我们做出错误设置异常变量时的不要重新抛出错误(请记住,这只是我们想让 finally 块可见的未捕获异常)。
finally (Exception main_exception)
{
try
{
//cleanup that may throw an error (absolutely unpredictably)
}
catch (Exception err)
{
//Instead of throwing another error,
//just add data to main exception mentioning that an error occurred in the finally block!
main_exception.Data.Add( "finally_error", err );
//main exception propagates from finally block normally, with additional data
}
}
As demonstrated above... the reason that I'd like the exception available in the finally block, is that if my finally block did catch an exception of its own, then instead of overwriting the main exception by throwing a new error (bad)or just ignoring the error (also bad), it could add the error as additional data to the original error.
如上所述......我希望在 finally 块中可用的异常的原因是,如果我的 finally 块确实捕获了自己的异常,那么不要通过抛出新错误(坏)来覆盖主要异常或者只是忽略错误(也很糟糕),它可以将错误作为附加数据添加到原始错误中。
回答by gedevan
Use logging...
使用日志...
try {
stream.write(buffer);
} catch(IOException ex) {
if (LOG.isErrorEnabled()) { // You can use log level whatever you want
LOG.error("Something wrong: " + ex.getMessage(), ex);
}
throw ex;
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ex) {
if (LOG.isWarnEnabled()) {
LOG.warn("Could not close in finally block", ex);
}
}
}
}
回答by Joel Coehoorn
You could always set a boolean flag in your catch(es). I don't know of any "slick" way to do it, but then I'm more of a .Net guy.
你总是可以在你的 catch(es) 中设置一个布尔标志。我不知道有什么“巧妙”的方法可以做到这一点,但我更像是一个 .Net 人。
回答by supercat
In vb.net, it's possible to use a "Catch...When" statement to grab an exception to a local variable without having to actually catch it. This has a number of advantages. Among them:
在 vb.net 中,可以使用“Catch...When”语句来捕获局部变量的异常,而不必实际捕获它。这有许多优点。其中:
- If nothing is going to 'ultimately' catch the exception, an unhandled exception trap will be fired from the spot of the original exception. Much nicer than having the debugger trap at the last rethrow, especially since information that might be needed for debugging won't yet have gone out of scope or been swept up by 'finally' statements.
- Although a rethrow won't clear the stack trace the way "Throw Ex" would, it will still often jinx the stack trace. If the exception isn't caught, the stack trace will be clean.
- 如果没有什么可以“最终”捕获异常,则会从原始异常的位置触发未处理的异常陷阱。比在最后一次重新抛出时使用调试器陷阱要好得多,特别是因为调试可能需要的信息尚未超出范围或被“最终”语句清除。
- 尽管重新抛出不会像“Throw Ex”那样清除堆栈跟踪,但它仍然经常会破坏堆栈跟踪。如果未捕获异常,则堆栈跟踪将是干净的。
Because this feature is unsupported in vb, it may be helpful to write a vb wrapper to implement the code in C (e.g. given a MethodInvoker and an Action(Of Exception), perform the MethodInvoker within a "Try" and the Action in a "Finally".
由于 vb 不支持此功能,因此编写 vb 包装器以在 C 中实现代码可能会有所帮助(例如,给定 MethodInvoker 和 Action(Of Exception),在“Try”中执行 MethodInvoker 并在“最后”。
One interesting quirk: it's possible for the Catch-When to see an exception which will end up getting overwritten by a Finally-clause exception. In some cases, this may be a good thing; in other cases it may be confusing. In any event, it's something to be aware of.
一个有趣的怪癖:Catch-When 可能会看到一个异常,该异常最终会被 finally 子句异常覆盖。在某些情况下,这可能是一件好事;在其他情况下,它可能会令人困惑。无论如何,这是需要注意的。

