最近,我们在Java服务器应用程序中遇到了一个问题,该应用程序正在抛出未被捕获的错误,因为Error是Throwable的一个单独的子类,而我们仅捕获了Exceptions。
我们通过捕获Throwables而不是Exceptions解决了紧迫的问题,但是这让我开始思考为什么您要捕获Exceptions而不是Throwables,因为那样您会错过Errors。
那么,当您可以捕获Throwables时,为什么还要捕获异常?
最近,我们在Java服务器应用程序中遇到了一个问题,该应用程序正在抛出未被捕获的错误,因为Error是Throwable的一个单独的子类,而我们仅捕获了Exceptions。
我们通过捕获Throwables而不是Exceptions解决了紧迫的问题,但是这让我开始思考为什么您要捕获Exceptions而不是Throwables,因为那样您会错过Errors。
那么,当您可以捕获Throwables时,为什么还要捕获异常?
Answers:
捕获错误后,这完全取决于您将如何处理错误。通常,捕获错误可能不应被视为“正常”异常流的一部分。如果您确实抓住了一个,那么您就不应该考虑“好像什么也没发生一样继续进行”,因为JVM(和各种库)将使用Errors来表示“确实发生了一些严重的事情,我们需要尽快关闭”。通常,最好在他们告诉您即将结束时听他们讲。
另一个问题是,是否可以从错误中恢复,可能取决于特定的虚拟机,而您可能无法控制。
就是说,在一些极端的情况下,安全和/或希望捕获Error或至少某些子类是可取的:
因此,最重要的是:如果您确实捕获了Throwable / Error而不是Exception,那应该是一个明确定义的情况,您知道自己在“做一些特别的事情”。
编辑:可能这很明显,但是我忘了说实际上,JVM可能实际上不会在Error上调用您的catch子句。我肯定已经看到Hotspot在捕获某些OutOfMemoryErrors和NoClassDefFoundError方面的尝试很轻松。
从Java API文档中:
该类
Exception及其子类是Throwable表示合理应用程序可能希望捕获的条件的。一个
Error是的子类Throwable,表示严重的问题,合理的应用程序不应该试图捕获。
错误通常是低级错误(例如,由虚拟机引发的错误),不应被应用程序捕获,因为可能无法进行合理的延续。
通常,错误是您无法从中恢复的问题,例如OutOfMemoryError。捕获它们无事可做,因此通常应让它们逃脱并关闭虚拟机。
我会走一条与其他人略有不同的路线。
在很多情况下,您想捕获Throwable(主要是记录/报告发生了恶事)。
然而,您需要小心并丢弃所有您无法处理的东西。
ThreadDeath尤其如此。
如果您遇到Throwable,请确保执行以下操作:
try {
...
} catch (SomeExceptionYouCanDoSomethingWith e) {
// handle it
} catch (ThreadDeath t) {
throw t;
} catch (Throwable t) {
// log & rethrow
}
在至少一种情况下,我认为您可能必须捕获一个抛出异常或通用异常-如果您正在运行一个单独的线程来执行任务,则可能想知道该线程的“运行”方法是否已捕获了某些异常是否例外。在这种情况下,您可能会执行以下操作:
public void run() {
try {
...
}
catch(Throwable t) {
threadCompletionError = t;
}
}
我真的不确定这是否是最好的方法,但是它有效。而且我有一个由JVM引发的“ ClassNotFound”错误,这是一个错误,而不是异常。如果我抛出异常,则我不确定如何在调用线程中捕获该异常(可能有一个方法,但我不知道-到目前为止)。
至于ThreadDeath方法,请不要调用“ Thread.stop()”方法。调用Thread.interrupt并让您的线程检查它是否被某人中断。
这篇文章不会使“检查异常很糟糕”的人高兴。但是,我要根据的答案是如何按照创建该语言的人的定义使用Java异常。
快速参考图:
您不应该捕获Exception的原因是它捕获了所有子类,包括RuntimeException。
您不应该捕获Throwable的原因是它捕获了所有子类,包括Error和Exception。
上述“规则”有例外情况(无双关语):
对于第二个,通常将主代码,事件处理代码和带有捕获的线程包装到Throwable中,然后检查异常的实际类型并酌情进行处理就足够了。
永远不要捕捉Throwable或,Error并且您通常也不应简单地捕捉通用的Exception。 Error通常,大多数合理的程序都无法从中恢复。如果您知道发生了什么,则可以从一个特定的错误中恢复,但是在这种情况下,您应该只捕获一个特定的错误,而不是一般的所有错误。
一个不被抓住的好理由Error是因为ThreadDeath。 ThreadDeath从理论上讲,这是一个相当正常的事件,可以从任何地方抛出(其他进程(如JVM本身也可以生成它)),并且它的全部目的是杀死线程。 ThreadDeath显然是一个,Error而不是Exception因为太多的人抓住了所有Exception。如果要抓住ThreadDeath,则必须将其重新抛出,以使线程实际终止。
如果您拥有对源代码的控制权,则可能应该对其进行重组以抛出Exception而不是Error。如果不这样做,您可能应该致电供应商并投诉。 Errors仅应保留为终端的东西,无法从它们中恢复。
通常,在编程时,您应该只捕获特定的异常(例如IOException)。在许多程序中,您可以看到一个非常高级的
try {
...
} catch(Exception e) {
...
}
映入所有错误可能是可恢复的,所有那些表明你的代码,例如一个bug InvalidArgumentException,NullPointerException。然后,由于JavaVM本身仍可以正常工作,因此您可以自动发送电子邮件,显示消息框或任何您喜欢的内容。
源自的一切都是Error非常糟糕的事情,您无能为力。问题是,抓住aOutOfMemoryError或a是否有意义VirtualMachineError。(这是JavaVM本身的错误,可能您甚至无法显示消息框或发送电子邮件)
您可能不Error应该从派生类,而应该从Exception或派生RuntimeException。
捕获Error没有意义。
错误用于指示您的应用程序确实出了什么问题,应该重新启动它。
例如,一个常见错误是
java.lang.OutOfMemoryError
有NOTHING当发生这种情况,你可以做。为时已晚,JVM已经用尽所有选项来获得更多内存,但这是不可能的。
请参阅此其他答案,以进一步了解这三种异常。