在Java中,是否有一种优雅的方法来检测在运行finally块之前是否发生了异常?处理“close()”语句时,通常需要在finally块中进行异常处理。理想情况下,我们希望保留两个异常并将它们传播(因为它们都可能包含有用的信息)。我能想到的唯一方法是在try-catch-finally范围之外设置一个变量来保存对抛出异常的引用。然后使用finally块中出现的任何内容传播“已保存”的异常。
有更优雅的方式吗?也许API调用会揭示这个?
以下是我正在谈论的一些粗略代码:
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
}
显然,还有一些其他类似的kludges可能涉及将异常保存为成员变量,从方法中返回等等......但我正在寻找更优雅的东西。
可能像Thread.getPendingException()
或类似的东西?就此而言,是否有其他语言的优雅解决方案?
这个问题实际上源自another question中的评论,提出了一个有趣的问题。
答案 0 :(得分:12)
关于在try / catch / finally范围之外设置变量的想法是正确的。
一次传播的异常不能超过一个。
答案 1 :(得分:6)
我将存储对Exception对象的引用,而不是使用布尔标志。 这样,您不仅可以检查是否发生了异常(如果没有异常,该对象将为null),但如果发生异常,您还可以在finally块中访问异常对象本身。您只需记住在所有catch块中设置错误对象(如果重新抛出错误)。
我认为这是一个缺少应该添加的C#语言特性。 finally块应该支持对基类Exception类的引用,类似于catch块如何支持它,以便引用传播异常可用于finally块。对于编译器来说,是一个简单的任务,手动 创建一个本地异常变量并记住 >在重新抛出错误之前手动设置其值,以及防止我们在不重新抛出错误时犯错误设置Exception变量(请记住,这只是未被捕获的异常我们想让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
}
}
如上所示...我想在finally块中提供异常的原因是,如果我的finally块确实捕获了它自己的异常,那么代替通过抛出覆盖主异常新错误(错误)或只是忽略错误(也是错误的),它可能会将错误添加为原始错误的附加数据。
答案 2 :(得分:2)
您总是可以在catch中设置一个布尔标志。我不知道有任何“光滑”的方式去做,但后来我更像是一个.Net人。
答案 3 :(得分:2)
使用记录...
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);
}
}
}
}
答案 4 :(得分:1)
在vb.net中,可以使用“Catch ... When”语句来获取局部变量的异常而无需实际捕获它。这具有许多优点。其中:
因为在vb中不支持此功能,所以编写一个vb包装器来实现C中的代码可能会有所帮助(例如,给定一个MethodInvoker和一个Action(异常),在“Try”和Action中执行MethodInvoker在“最后”。
一个有趣的怪癖:Catch-When有可能看到一个异常,最终会被Finally子句异常覆盖。在某些情况下,这可能是一件好事;在其他情况下,它可能会令人困惑。无论如何,这是值得注意的事情。