我们最近遇到了一个Java服务器应用程序的问题,其中应用程序抛出了未捕获的错误,因为Error是Throwable的一个单独的子类,我们只捕获异常.
我们通过捕获Throwables而不是Exceptions解决了当前的问题,但是这让我想到为什么你想要捕获Exceptions而不是Throwables,因为你会错过错误.
所以,当你能捕获Throwables时,你为什么要捕捉异常呢?
从Java API文档:
该类
Exception
及其子类是一种Throwable
表示合理的应用程序可能想要捕获的条件的形式.An
Error
是一个子类Throwable
,表示合理的应用程序不应该试图捕获的严重问题.
错误通常是低级别的(例如,由虚拟机引发),并且不应该被应用程序捕获,因为可能无法合理地继续.
这一切都取决于你在捕获它后会对错误做些什么.通常,捕获错误可能不应被视为"正常"异常流程的一部分.如果你确实抓到一个,你不应该考虑"继续进行,好像什么也没发生",因为JVM(和各种库)将使用错误作为一种方式来表示"发生了一些非常严重的事情,我们需要尽快关闭".一般来说,最好在他们告诉你结束的时候听他们说.
另一个问题是,错误的可恢复性与否可能取决于特定的虚拟机,这是您可能控制或不能控制的.
也就是说,有一些极端情况是安全和/或希望捕获错误,或者至少是某些子类:
在某些情况下,您确实希望停止正常的流程:例如,如果您在Servlet中,您可能不希望Servlet运行器的默认异常处理程序向全世界宣布您有OutOfMemoryError,无论是不是你可以从它恢复.
有时,如果JVM可以从错误原因中干净地恢复,则会抛出错误.例如,如果在尝试分配数组时发生OutOfMemoryError,至少在Hotspot中,您似乎可以安全地从中恢复.(当然还有其他情况可能会抛出OutOfMemoryError而不能安全地尝试使用它.)
所以底线是:如果你确实捕获了Throwable/Error而不是Exception,那么它应该是一个明确定义的情况,你知道你正在"做一些特殊的事情".
编辑:可能这很明显,但我忘了说在实践中,JVM实际上可能不会在Error上调用catch子句.我肯定看到Hotspot明显地掩盖了捕获某些OutOfMemoryErrors和NoClassDefFoundError的尝试.
通常错误是您无法恢复的问题,例如OutOfMemoryError.通过捕获它们无关紧要,因此您通常应该让它们逃脱,并关闭虚拟机.
很多其他答案都是在狭隘地看待事物.
正如他们所说,如果你正在编写应用程序代码,你不应该抓住Throwable.你无法做任何事情,所以最好让周围的系统(JVM或框架)来处理这些问题.
但是,如果您正在编写"系统代码",如框架或其他低级代码,那么您可能非常希望捕获Throwable.原因是尝试在日志文件中报告异常.在某些情况下,您的日志记录将失败,但在大多数情况下,它将成功,您将获得解决问题所需的信息.完成日志记录后,应该重新抛出,终止当前线程或退出整个JVM.
我会走一条与其他人略有不同的路线。
在很多情况下,您想捕获Throwable(主要是记录/报告发生了恶事)。
但是,您需要小心并丢弃所有您无法处理的东西。
对于ThreadDeath尤其如此。
如果您遇到Throwable,请确保执行以下操作:
try { ... } catch (SomeExceptionYouCanDoSomethingWith e) { // handle it } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // log & rethrow }