在 CLR(C#、VB.NET 等使用的运行时)中,有一种方法可以注册回调,以便在引发未处理的异常时调用该回调。
Java中有类似的东西吗?
我猜测它可能是一些 API,您可以将一个对象传递给该 API,该对象使用单个方法实现某个接口(interface)。当抛出异常并且堆栈上没有匹配的 catch
时,运行时将调用注册对象上的方法,并传递异常对象。
这将允许程序员保存堆栈跟踪。它还允许他们调用System.exit
,以停止finally
block 仅针对未处理的异常执行。
更新 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
block 的执行。
果然,在 Windows 7 上运行的 NET 3.5 和 4.0 中,我在输出中没有看到“finally isexecution”字符串。但是,如果我注释掉 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) 中运行它时,我看到:
- 终于执行
- 未捕获的异常
换句话说,JRE 在执行未捕获异常处理程序之前执行 finally
block 。
有关为什么这很重要的一些背景信息,这里有一个更复杂的示例:
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
block (在实际程序中,它将位于单独的方法中),并且它会尝试访问文件系统。出了问题。然后再往上一点,假设我捕获了 IOException,因为它们对我来说没什么大不了的。
不用说,输出是:
- 终于执行
- 捕获 IOException
- 程序继续运行,就像什么都没发生一样...
所以现在我不小心造成了一种情况,其中严重的错误对我来说是隐藏的。
有两种解决方案:
- 以某种方式确保
finally
阻止永远抛出,因为它们无法在本地判断它是否安全。这是一种耻辱,因为它们完全可以在正常执行路径上抛出异常,即当它们没有运行以响应先前的异常时。 - 告诉运行时,当出现未捕获的异常时,我不希望它运行
finally
block 。
如果可以的话,后者当然是我的首选。
最佳答案
参见 Thread.setUncaughtExceptionHandler()
.
更新:事实证明,“未处理/未捕获的异常”在 C# 和 Java 中的含义似乎略有不同。乍一看,您描述的行为在 Java 中似乎是正常的(对您来说不幸的是):
Set the handler invoked when this thread abruptly terminates due to an uncaught exception.
即当异常已经从用户代码传播到线程中时,将调用未捕获的异常处理程序。这意味着它留下了包含 finally
的方法 block ,已按时执行。
据我所知finally
block 总是在Java中运行,因此您的选项2不可行。
下面讨论的结论
选项 1 - 不向 finally
中扔任何东西 block - 尽管有限制,但似乎是唯一真正的长期解决方案。
对于日志记录部分,有一个选项 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");
}
罢工><罢工>罢工>
关于Java 相当于 CLR 的 UnhandledException 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3086125/