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

时间:2008-10-08 20:22:06

标签: java exception-handling

在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中的评论,提出了一个有趣的问题。

5 个答案:

答案 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”语句来获取局部变量的异常而无需实际捕获它。这具有许多优点。其中:

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

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

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