何时捕获java.lang.Error?


Answers:


101

通常,永远不会。

但是,有时您需要捕获特定的错误。

如果您正在编写框架式的代码(加载第3方类),那么捕获LinkageError(未找到类定义,未满足的链接,不兼容的类更改)可能是明智的。

我还看到了一些愚蠢的第三方代码抛出的子类Error,因此您也必须处理这些子类。

顺便说一下,我不确定无法从中恢复OutOfMemoryError


3
我必须完全执行加载DLL的操作,如果未正确配置DLL将会失败。在此应用程序中不是致命错误。
MarioOrtegón08年

7
有时,捕获OutOfMemoryError是有意义的-例如,当您创建大型数组列表时。
SpaceTrucker 2013年

3
@SpaceTrucker:这种方法在多线程应用程序中是否很好用,还是存在其他线程较小的分配因此而失败的重大风险?…大概仅在您的数组足够小以至于可以分配但对其他人一无所有的情况下。
PJTraill 2015年

@PJTraill我不确定。这将需要一些现实世界的统计样本。我以为我已经看过这样的代码,但是不记得它在哪里。
SpaceTrucker

51

决不。您永远不能确保应用程序能够执行下一行代码。如果得到了OutOfMemoryError,则不能保证可以可靠地进行任何操作。捕获RuntimeException并检查异常,但不捕获错误。

http://pmd.sourceforge.net/rules/strictexception.html


27
永远不要把话说绝了。我们有测试代码可以“断言”;然后捕获AssertionError以确保设置了-ea标志。除此之外...是的,可能永远不会;-)
Outlaw程序员

3
将请求传递给工作线程的服务器应用程序怎么样。在工作线程上捕获Throwable来捕获任何错误可能没有任何意义,并且至少尝试记录发生了什么问题?

11
永远不要...除非您绝对需要。从来没有一个好词,规则总是有例外。如果要构建框架,即使只是记录日志,也不太可能必须捕获并处理某些错误。
罗宾

来自第三方库方法的错误(例如NoSuchMethodError)如何?
ha9u63ar 2013年

@OutlawProgrammer只是为了记录,还有其他方法可以执行相同的测试:boolean assertionsEnabled = false; assert assertionsEnabled = true;
shmosel

16

通常,您应该始终捕获java.lang.Error并写入日志或将其显示给用户。我在支持部门工作,每天都看到程序员无法分辨程序中发生了什么。

如果您有守护程序线程,则必须防止其终止。在其他情况下,您的应用程序将正常运行。

您应该只java.lang.Error在最高级别上捕获。

如果查看错误列表,您会发现大多数错误都可以解决。例如ZipError,在读取损坏的zip文件时发生a 。

最常见的错误是OutOfMemoryErrorNoClassDefFoundError,在大多数情况下都是运行时问题。

例如:

int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];

可以产生一个,OutOfMemoryError但这是一个运行时问题,没有理由终止您的程序。

NoClassDefFoundError大多数情况是在没有库或使用其他Java版本的情况下发生的。如果它是程序的可选部分,则不应终止程序。

我可以举很多例子说明为什么Throwable在顶层抓住一个好主意并产生有用的错误消息。


我怀疑在这种情况下更好的办法是使守护进程失败并发出适当的警报,然后使其有可能像失败的jvm上的一些虚幻的鬼魂一样存活,这可能会给人以虚假的借口,说它确实还活着并且在做些什么
安德鲁Norman

OutOfMemoryError不是运行时错误,不能保证应用程序可以从中恢复。如果幸运的话,您可以加入OOM,new byte[largeNumber]但是如果该分配不足以导致OOM,则可以在下一行或下一个线程中触发它。这是运行时问题,因为如果length输入不受信任,则应在调用之前对其进行验证new byte[]
Jeeyoung Kim

NoClassDefFoundError可以在任何地方发生,因为在已编译的Java代码找不到类时会调用它。如果您的JDK配置不正确,它可能会因尝试使用java.util.*类而触发,并且实际上无法针对它进行编程。如果可以选择包括依赖项,则应使用ClassLoader它来检查是否存在依赖项(引发)ClassNotFoundException
Jeeyoung Kim

1
ZipError表示包含类的jar文件是损坏的zip文件。这是一个非常严重的问题,在这一点上,您不能相信要执行的任何代码,并且尝试从中“恢复”是不负责任的事情。
Jeeyoung Kim

2
通常,捕获java.lang.Errorjava.lang.Throwable在顶层尝试尝试执行某些操作可能会有所帮助-说记录错误消息。但是在那一点上,不能保证会被执行。如果您的JVM是OOMing,则尝试记录可能会分配更多Strings,从而触发另一个OOM。
Jeeyoung Kim

15

在多线程环境中,您最经常想抓住它!当您捕获它时,将其记录下来并终止整个应用程序!如果您不这样做,则可能正在执行某些关键部分的线程将被终止,并且该应用程序的其余部分将认为一切正常。除此之外,还可能发生许多不需要的情况。一个最小的问题是,如果其他线程由于一个线程不起作用而开始引发某些异常,那么您将无法轻松找到问题的根源。

例如,通常循环应为:

try {
   while (shouldRun()) {
       doSomething();
   }
}
catch (Throwable t) {
   log(t);
   stop();
   System.exit(1);
}

即使在某些情况下,您也可能希望以不同的方式处理不同的错误,例如,在OutOfMemoryError上,您可以定期关闭应用程序(甚至可以释放一些内存,然后继续),而在其他情况下,则无能为力。


1
捕获OutOfMemoryError 并继续而不是立即存在是不明智的,因为您的程序随后处于未定义状态
Raedwald

1
调用系统时,捕获可抛出异常将导致意外后果,即杀死JVM上运行的所有内容,而不仅仅是相关应用程序。对于可能与其他Web应用程序托管在应用服务器上的Web应用程序,通常不是一个好习惯(更不用说会影响应用服务器本身)。
Andrew Norman

9

非常稀有。

我只想在线程的顶层说,以便ATTEMPT发出一条消息,说明线程快死了。

如果您在为您执行此类操作的框架中,则将其留给框架。


6

几乎从不。错误被设计为应用程序通常无能为力的问题。唯一的例外可能是处理错误的表示形式,但根据错误的不同,可能无法按计划进行。


6

一个Error通常不应该被捕获,因为它表明绝不应该出现的异常情况

Error该类的Java API规范中:

An Error是的子类,Throwable 它指示合理的应用程序不应尝试捕获的严重问题。大多数此类错误是异常情况。[...]

不需要方法在throws子句中声明在方法执行期间可能抛出但未被捕获的Error的任何子类,因为这些错误是不应该发生的异常情况。

如规范所提到的,Error仅在可能的情况下抛出an,当Error发生时,应用程序几乎无能为力,并且在某些情况下,Java虚拟机本身可能处于不稳定状态(例如VirtualMachineError

尽管an Error是它的子类Throwable,意味着它可以被try-catch子句捕获,但实际上并不需要它,因为当Error JVM抛出。

Java语言规范第二版的11.5“异常层次结构”中也有关于此主题的一小节。


6

如果您疯狂到足以创建一个新的单元测试框架,那么您的测试运行器可能需要捕获任何测试用例抛出的java.lang.AssertionError。

否则,请参阅其他答案。


5

还有其他几种情况,如果您发现错误,则必须将其重新抛出。例如,永远不要捕获ThreadDeath,这可能会导致很大的问题,因为您是在包含环境中(例如应用程序服务器)捕获它的:

应用程序只有在异步终止后必须清除的情况下,才应捕获此类的实例。如果ThreadDeath被某个方法捕获,则将其重新抛出以使线程实际上死亡是很重要的。


2
实际上这不是问题,因为您根本Errors。
孟买

4

非常非常罕见

我仅针对一个非常非常特定的已知案例进行了此操作。例如,如果两个独立的ClassLoader可能引发java.lang.UnsatisfiedLinkError加载相同的DLL。(我同意我应该将JAR移到共享的类加载器中)

但是最常见的情况是您需要登录才能知道用户抱怨时发生了什么。您想要向用户发送消息或弹出窗口,而不是默默地死掉。

即使是使用C / C ++的程序员,他们也会弹出错误并在退出之前告诉人们一些不了解的内容(例如,内存故障)。


3

在Android应用程序中,我捕获到java.lang.VerifyError。我正在使用的库无法在具有旧版操作系统的设备中使用,并且库代码将引发此类错误。我当然可以通过在运行时检查OS的版本来避免该错误,但是:

  • 受支持的最早的SDK将来可能会针对特定库进行更改
  • try-catch错误块是更大的回退机制的一部分。一些特定的设备尽管应该支持该库,但是会引发异常。我捕获了VerifyError和所有异常以使用后备解决方案。

3

在测试环境中捕获java.lang.AssertionError非常方便...


您想增加什么价值?
lpapp

添加测试套件的价值不会中止
Jono

2

理想情况下,我们不应该处理/捕获错误。但是,根据框架或应用程序的需求,有时可能需要执行此操作。说我有一个XML Parser守护程序,该守护程序实现了消耗更多内存的DOM Parser。如果有诸如Parser线程之类的需求,当它获取OutOfMemoryError时不应该被杀死,它应该处理它并向应用程序/框架的管理员发送消息/邮件。


1

理想情况下,我们绝不应该在Java应用程序中捕获Error,因为这是异常情况。该应用程序将处于异常状态,并可能导致堆积或给出一些严重错误的结果。


1

在进行断言的单元测试中捕获错误可能是适当的。如果有人禁用断言或以其他方式删除断言,您想知道


1

当JVM无法按预期运行或即将运行时,将出现错误。如果捕获到错误,则不能保证catch块将运行,甚至不能保证一直运行到最后。

它还将取决于正在运行的计算机,当前的内存状态,因此无法测试,尝试并尽力而为。您只会得到一个仓促的结果。

您还将降低代码的可读性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.