我们最近遇到了Java服务器应用程序的问题,其中应用程序抛出了未捕获的错误,因为Error是Throwable的一个独立子类,我们只捕获异常。
我们通过捕获Throwables而不是Exceptions来解决当前的问题,但这让我想到为什么你想要捕获Exceptions而不是Throwables,因为你会错过错误。
所以,为什么你想捕捉异常,当你可以抓住Throwables ?
答案 0 :(得分:58)
来自Java API文档:
类
Exception
及其子类是Throwable
的一种形式,表示合理的应用程序可能想要捕获的条件。
Error
是Throwable
的子类,表示合理的应用程序不应该试图捕获的严重问题。
错误通常是低级别的(例如,由虚拟机引发),并且不应该被应用程序捕获,因为可能无法合理延续。
答案 1 :(得分:57)
这一切都取决于一旦你发现错误你将要做什么。通常,捕获错误可能不应被视为“正常”异常流程的一部分。如果你确实抓到一个,你不应该考虑“继续进行,好像什么也没发生”,因为JVM(和各种库)将使用错误作为一种信号“发生真正严重的事情并且我们需要尽快关闭“。一般来说,最好在他们告诉你结束的时候听他们说。
另一个问题是,错误的可恢复性可能取决于特定的虚拟机,这可能是您可能无法控制的。
尽管如此,有一些极端情况是安全和/或希望捕获错误,或者至少是某些子类:
所以最重要的是:如果你确实捕获了Throwable / Error而不是Exception,那么它应该是一个明确定义的情况,你知道你正在“做一些特别的事情”。
编辑:可能这很明显,但我忘了说实际上, JVM可能实际上没有在Error上调用你的catch子句。我肯定看到Hotspot明显地掩盖了捕获某些OutOfMemoryErrors和NoClassDefFoundError的尝试。
答案 2 :(得分:6)
通常错误是您无法恢复的问题,例如OutOfMemoryError。抓住它们无关紧要,所以你应该让它们逃脱,然后关闭虚拟机。
答案 3 :(得分:5)
很多其他答案都在过于狭隘地看待事情。
正如他们所说,如果你正在编写应用程序代码,你不应该抓住Throwable。你无法做任何事情,所以最好让周围的系统(JVM或框架)来处理这些问题。
但是,如果您正在编写“系统代码”,如框架或其他低级代码,那么您可能非常希望捕获Throwable。原因是尝试报告异常,可能在日志文件中。在某些情况下,您的日志记录将失败,但在大多数情况下,它将成功,您将获得解决问题所需的信息。完成日志记录后,应该重新抛出,终止当前线程或退出整个JVM。
答案 4 :(得分:4)
我会和其他人略有不同。
在很多情况下,你想捕捉Throwable(主要是记录/报告发生了邪恶的事情)。
然而,你需要小心并重新抛出任何你无法处理的事情。
ThreadDeath尤其如此。
如果您遇到Throwable,请务必执行以下操作:
try {
...
} catch (SomeExceptionYouCanDoSomethingWith e) {
// handle it
} catch (ThreadDeath t) {
throw t;
} catch (Throwable t) {
// log & rethrow
}
答案 5 :(得分:2)
请勿 永远 抓住Throwable
或Error
,您通常也不应只是抓住通用Exception
。 Error
通常是最合理的程序无法恢复的东西。如果您知道发生了什么,您可能能够从一个特定错误中恢复,但在这种情况下,您应该只捕获 一个特定错误而不是所有错误。
不抓住Error
的好理由是ThreadDeath
。 ThreadDeath
是一个相当正常的事件,理论上可以从任何地方抛出(其他进程,如JVM本身可以生成它),它的全部意义是杀死你的线程。 ThreadDeath
明确是Error
而非Exception
,因为有太多人会抓住所有Exception
。如果你曾经抓住ThreadDeath
,你必须重新抛出它,以便你的线程真的死了。
如果您可以控制源,则应该重新构建它以抛出Exception
而不是Error
。如果你不这样做,你应该打电话给供应商并抱怨。 Error
应仅保留给那些没有可能从中恢复的终端。
答案 6 :(得分:2)
这篇文章不会让“经过检查的例外情况不好”让人高兴。但是,我所依据的是如何根据创建该语言的人员的定义使用Java异常。
快速参考图表:
你不应该捕获Exception的原因是它捕获了所有子类,包括RuntimeException。
你不应该捕获Throwable的原因是它捕获了所有子类,包括Error和Exception。
上述“规则”中有例外(没有双关语):
对于第二个,通常只需将main,事件处理代码和带有catch的线程包装到Throwable,然后检查异常的实际类型并在适当时处理它。
答案 7 :(得分:2)
至少有一种情况,我认为您可能必须捕获throwable或一般异常 - 如果您正在运行单独的线程来执行任务,您可能想知道该线程的“run”方法是否有一些例外。在这种情况下,你可能会做这样的事情:
public void run() {
try {
...
}
catch(Throwable t) {
threadCompletionError = t;
}
}
我真的不确定这是否是最好的方法,但它确实有效。我正在JVM引发一个“ClassNotFound”错误,这是一个错误而不是例外。如果我抛出异常,我不知道如何在调用线程中捕获它(可能有一个方法,但我不知道它 - 但是)。
对于ThreadDeath方法,不要调用“Thread.stop()”方法。调用Thread.interrupt并让你的线程检查它是否被某人打断了。
答案 8 :(得分:1)
通常在编程时,您应该只捕获特定的异常(例如IOException
)。在许多程序中,您可以看到非常高级的
try {
...
} catch(Exception e) {
...
}
捕获所有可以恢复的错误以及表示代码中存在错误的所有错误,例如: InvalidArgumentException
,NullPointerException
。然后,您可以自动发送电子邮件,显示消息框或任何您喜欢的内容,因为JavaVM本身仍然可以正常工作。
从Error
派生的一切都是非常糟糕的,你不能做任何反对。问题是,如果抓住OutOfMemoryError
或VirtualMachineError
是有意义的。 (这是JavaVM本身的错误,可能你甚至无法显示消息框或发送电子邮件)
您可能不应该是从Error
派生的类,您应该从Exception
或RuntimeException
派生。
答案 9 :(得分:1)
我知道这可能是违反直觉的,但仅仅因为你可以捕捉到各种各样的例外情况和可抛出错误而且不意味着你应该这样做。
过度攻击java.lang.Exception会导致应用程序出现严重错误 - 因为意外异常从未冒泡,在开发/测试期间从未被捕获等。
最佳实践:只抓住
答案 10 :(得分:1)
一般来说,如果只是为了能够正确报告错误,那么尝试捕获错误是合理的。
但是,我认为有时候捕获错误并且不报告它是合适的。我指的是UnsatisfiedLinkError。在JAI中,库出于性能原因使用一些本机库来实现大多数操作符,但是如果库无法加载(不存在,格式错误,平台不受支持),库仍将继续运行,因为它将回退到仅Java模式
答案 11 :(得分:0)
稍微偏离主题,但您可能还想查看有关例外情况的this very good article。
答案 12 :(得分:0)
为什么不赶上他们?然后记录它们,至少你知道你有错误。所以最好只捕获Throwable / s而不是Exception / s。
答案 13 :(得分:-1)
抓住错误没有意义。
错误用于表示应用程序中出现了问题,应该重新启动。
例如,一个常见错误是
java.lang.OutOfMemoryError
当发生这种情况时,你可以做 NOTHING 。已经为时已晚,JVM已经耗尽了所有选项以获得更多内存,但这是不可能的。
请参阅此其他答案,以了解有关three kinds of exceptions。
的更多信息