2

在 CLR(C#、VB.NET 等使用的运行时)中,有一种方法可以注册回调,以便在引发未处理的异常时调用。

Java中有类似的东西吗?

我猜想它可能是某个 API,您可以将一个对象传递给该 API,该对象使用单个方法实现某个接口。当抛出异常并且catch堆栈上没有匹配时,运行时将调用注册对象上的方法,并传递异常对象。

这将允许程序员保存堆栈跟踪。它还允许他们调用System.exit, 来停止finally仅针对未处理的异常执行的块。

更新 1。

为了说明这一点,这里有一个 C# 示例:

// register custom handler for unhandled exceptions
AppDomain.CurrentDomain.UnhandledException += (sender, evt) =>
{
    Console.WriteLine("unhandled exception");
    Environment.FailFast(null);
};

try
{
    throw new NullReferenceException();
}
finally
{
    Console.WriteLine("finally is executing");
}

关键是通过调用Environment.FailFast(null)我可以阻止finally块执行。

果然,在 Windows 7 上运行的 NET 3.5 和 4.0 中,我没有在输出中看到“finally is execution”字符串。但是如果我注释掉这个FailFast调用,那么我会在输出中看到那个字符串。

更新 2。

根据迄今为止的答案,这是我在 Java 中重现此问题的尝试。

// register custom handler for unhandled exceptions
Thread.currentThread().setUncaughtExceptionHandler(

    new Thread.UncaughtExceptionHandler() {

        public void uncaughtException(
                final Thread t, final Throwable e) {

            System.out.println("Uncaught exception");
            System.exit(0);
        }
    }
);

try
{
    throw new NullPointerException();
}
finally
{
    System.out.println("finally is executing");
}

当我在 Java 6 (1.6.0_18) 中运行它时,我看到:

  • finally 正在执行
  • 未捕获的异常

换句话说,JREfinally在执行未捕获异常处理程序之前执行块。

关于为什么这很重要的一些背景,这里有一个更复杂的例子:

try
{
    try
    {
        throw new NullPointerException();
    }
    finally
    {
        System.out.println("finally is executing");
        throw new java.io.IOException();
    }
}
catch (java.io.IOException x)
{
    System.out.println("caught IOException");
}

System.out.println("program keeps running as if nothing had happened...");

所以有一个严重的错误,我希望我的程序停止并记录堆栈跟踪。但在我能做到这一点之前,堆栈上的某处有一个中间finally块(在一个真正的程序中它会在一个单独的方法中),它会尝试访问文件系统。出了点问题。然后再往上一点假设我有一个问题,IOException因为它们对我来说没什么大不了的。

不用说,输出是:

  • finally 正在执行
  • 捕获 IOException
  • 程序继续运行,就好像什么都没发生过一样……

所以现在我无意中创造了一个严重的错误对我隐藏的情况。

有两种解决方案:

  • 以某种方式确保finally永远不会抛出,因为它们无法在本地判断它是否安全。这是一种耻辱,因为它们完全可以在正常执行路径上抛出,即当它们没有运行以响应先前的异常时。
  • 告诉运行时我不希望它finally在有未捕获的异常时运行块。

如果可以的话,后者当然是我的首选。

4

1 回答 1

7

Thread.setUncaughtExceptionHandler()

更新:事实证明,“未处理/未捕获的异常”在 C# 和 Java 中的含义似乎略有不同。乍一看,您描述的行为在 Java 中似乎(不幸的是)是正常的:

设置当此线程由于未捕获的异常而突然终止时调用的处理程序。

即,当异常已经从用户代码传播到线程中时,调用未捕获的异常处理程序。这意味着它离开了包含finally已适当执行的​​块的方法。

AFAIKfinally始终在 Java 中运行,因此您的选项 2 不可行。

以下讨论的结论

选项 1 - 不扔任何东西finally- 尽管有限制,但似乎是唯一真正的长期解决方案。

对于简单的日志记录部分,有一个选项 3:

try
{
    try
    {
        throw new NullPointerException();
    }
    catch (Exception x)
    {
        System.out.println("caught Exception" + x.getMessage());
        x.printStackTrace();
        throw x; // keep original behaviour
    }
    finally
    {
        System.out.println("finally is executing");
        throw new java.io.IOException();
    }
}
catch (java.io.IOException x)
{
    System.out.println("caught IOException");
}

于 2010-06-21T15:52:04.113 回答