在我输入finally块之前是否可以检测是否发生了exception?

在Java中,是否有一种优雅的方法来检测在运行finally块之前是否发生了exception? 处理“close()”语句时,通常需要在finally块中进行exception处理。 理想情况下,我们希望保留两个exception并将它们传播(因为它们都可能包含有用的信息)。 我能想到的唯一方法就是在try-catch-finally范围之外设置一个变量来保存对抛出exception的引用。 然后使用finally块中出现的任何内容传播“已保存”的exception。

这样做有更优雅的方式吗? 也许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可能涉及将exception保存为成员变量,从方法返回等等……但我正在寻找更优雅的东西。

也许像Thread.getPendingException()或类似的东西? 就此而言,是否有其他语言的优雅解决方案?

这个问题实际上是从另一个提出一个有趣问题的问题的评论中产生的。

关于在try / catch / finally范围之外设置变量的想法是正确的。

一次传播的exception不能超过一个。

我将存储对Exception对象的引用,而不是使用布尔标志。 这样,您不仅可以检查是否发生了exception(如果没有exception,该对象将为null),但如果发生exception,您还可以在finally块中访问exception对象本身。 您只需记住在所有catch块中设置错误对象(如果重新抛出错误)。

我认为这是一个缺少应该添加的C#语言function。 finally块应该支持对基类Exception类的引用,类似于catch块如何支持它,以便finally块可以使用对传播exception的引用。 对于编译器来说 ,这将是一项简单的任务,为 我们节省手动创建本地Exception变量的工作 ,并记住在重新抛出错误之前手动设置其值,以及防止我们在设置Exception变量时犯错 。不要重新抛出错误(记住,它只是我们想要对finally块可见的未捕获的exception)。

 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块中提供exception的原因是,如果我的finally块确实捕获了它自己的exception,那么不要通过抛出新的错误覆盖主exception(坏)或者只是忽略错误(也是坏的) ,它可以将错误作为附加数据添加到原始错误中。

您总是可以在catch中设置一个布尔标志。 我不知道有任何“光滑”的方式去做,但后来我更像是一个.Net人。

使用记录……

 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); } } } } 

在vb.net中,可以使用“Catch … When”语句来获取局部变量的exception而无需实际捕获它。 这具有许多优点。 其中:

  1. 如果没有任何东西’最终’捕获exception,将从原始exception的位置触发未处理的exception陷阱。 比在最后一次重新抛出时使用调试器陷阱要好得多,特别是因为调试可能需要的信息不会超出范围或被“最终”语句扫除。
  2. 虽然重新抛出不会像“Throw Ex”那样清除堆栈跟踪,但它仍然经常使堆栈跟踪变得混乱。 如果未捕获exception,则堆栈跟踪将是干净的。

因为在vb中不支持此function,所以编写一个vb包装器来实现C中的代码可能会有所帮助(例如,给定一个MethodInvoker和一个Action(exception),在“Try”中执行MethodInvoker并在“最后”。

一个有趣的怪癖:Catch-When有可能看到一个exception,最终会被Finally子句exception覆盖。 在某些情况下,这可能是一件好事; 在其他情况下,它可能会令人困惑。 无论如何,这是值得注意的。