22

在 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()或类似的东西?就此而言,其他语言是否有优雅的解决方案?

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

4

5 回答 5

12

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

一次传播的异常不能超过一个。

于 2008-10-08T20:25:41.097 回答
6

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

我认为这是应该添加的缺少的 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
    }
}

如上所示......我希望 finally 块中可用的异常的原因是,如果我的 finally 块确实捕获了它自己的异常,那么不要通过抛出新错误(坏)来覆盖主要异常或者只是忽略错误(也很糟糕),它可以将错误作为附加数据添加到原始错误中。

于 2009-08-30T19:23:03.887 回答
2

你总是可以在你的捕获中设置一个布尔标志。我不知道有什么“巧妙”的方法可以做到这一点,但我更像是一个 .Net 人。

于 2008-10-08T20:23:20.353 回答
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);
            }
        }
    }
}
于 2008-10-10T09:21:12.357 回答
1

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

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

由于 vb 不支持此功能,因此编写 vb 包装器以在 C 中实现代码可能会有所帮助(例如,给定 MethodInvoker 和 Action(Of Exception),在 "Try" 中执行 MethodInvoker,在 "最后”。

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

于 2010-12-21T18:32:20.890 回答