【发布时间】:2010-09-26 01:13:06
【问题描述】:
在什么情况下应该在应用程序中捕获java.lang.Error?
【问题讨论】:
标签: java exception error-handling exception-handling
在什么情况下应该在应用程序中捕获java.lang.Error?
【问题讨论】:
标签: java exception error-handling exception-handling
一般来说,从不。
但是,有时您需要捕获特定错误。
如果您正在编写类似于框架的代码(加载第 3 方类),最好捕获 LinkageError(未找到类定义、不满意的链接、不兼容的类更改)。
我还看到一些愚蠢的第 3 方代码抛出 Error 的子类,所以你也必须处理这些。
顺便说一句,我不确定是否无法从OutOfMemoryError 中恢复。
【讨论】:
从来没有。您永远无法确定应用程序能够执行下一行代码。如果你得到OutOfMemoryError,你就有no guarantee that you will be able to do anything reliably。捕获 RuntimeException 和已检查的异常,但从不捕获错误。
【讨论】:
boolean assertionsEnabled = false; assert assertionsEnabled = true;
通常,您应该始终捕获java.lang.Error 并将其写入日志或显示给用户。我在支持部门工作,每天都看到程序员无法分辨程序中发生了什么。
如果你有一个守护线程,那么你必须防止它被终止。在其他情况下,您的应用程序将正常工作。
您应该只在最高级别捕获java.lang.Error。
如果您查看错误列表,您会发现大部分都可以处理。例如,在读取损坏的 zip 文件时会出现 ZipError。
最常见的错误是OutOfMemoryError 和NoClassDefFoundError,在大多数情况下它们都是运行时问题。
例如:
int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];
可以生成OutOfMemoryError,但这是一个运行时问题,没有理由终止您的程序。
NoClassDefFoundError 主要发生在不存在库或使用另一个 Java 版本时。如果它是您程序的可选部分,那么您不应终止您的程序。
我可以举更多例子说明为什么在顶层捕获 Throwable 并生成有用的错误消息是个好主意。
【讨论】:
OutOfMemoryError 不是运行时错误,无法保证应用程序可以从中恢复。如果幸运的话,您可能会在new byte[largeNumber] 中获得 OOM,但如果该分配不足以导致 OOM,则可能会在下一行或下一个线程中触发。这是运行时问题,因为如果 length 是不受信任的输入,则应在调用 new byte[] 之前对其进行验证。
NoClassDefFoundError 可以出现在任何地方,因为当编译的java 代码找不到类时会调用它。如果您的 JDK 配置错误,它可能会因尝试使用 java.util.* 类而触发,并且几乎不可能针对它进行编程。如果您可以选择包含依赖项,则应使用ClassLoader 来检查它是否存在,这会抛出ClassNotFoundException。
ZipError 表示包含类的 jar 文件是损坏的 zip 文件。这是一个非常严重的问题,此时您不能信任任何被执行的代码,尝试从中“恢复”将是不负责任的事情。
java.lang.Error 或java.lang.Throwable 并尝试用它做一些事情可能会有所帮助——比如记录一条错误消息。但在这一点上,不能保证这会得到执行。如果您的 JVM 正在 OOM,则尝试记录可能会分配更多 Strings,从而触发另一个 OOM。
在多线程环境中,您最常想捕捉它!当您捕获它时,记录它并终止整个应用程序!如果您不这样做,则可能正在执行某些关键部分的某些线程将死掉,而应用程序的其余部分将认为一切正常。因此,可能会发生许多不需要的情况。 一个最小的问题是,如果其他线程由于一个线程不工作而开始抛出一些异常,您将无法轻松找到问题的根源。
例如,通常循环应该是:
try {
while (shouldRun()) {
doSomething();
}
}
catch (Throwable t) {
log(t);
stop();
System.exit(1);
}
即使在某些情况下,您也希望以不同的方式处理不同的错误,例如,在 OutOfMemoryError 上,您将能够定期关闭应用程序(甚至可能释放一些内存,然后继续),在其他一些情况下,您没有太多可以。
【讨论】:
OutOfMemoryError 并继续而不是立即存在是不明智的,因为your program is then in an undefined state。
很少。
我会说只在线程的顶层,以便尝试发出一条消息,说明线程死亡的原因。
如果您所在的框架为您执行此类操作,请将其留给框架。
【讨论】:
几乎没有。错误被设计为应用程序通常无法解决的问题。唯一的例外可能是处理错误的呈现,但即使这样也可能不会按计划进行,具体取决于错误。
【讨论】:
Error 通常不应被捕获,因为它表示不应该发生的异常情况。
来自Error 类的 Java API 规范:
Error是Throwable的子类 这表明严重的问题 合理的应用不应该尝试 去抓。大多数此类错误是 异常情况。 [...]方法不需要声明 它的 throws 子句的任何子类 期间可能抛出的错误 执行该方法但不执行 捕获,因为这些错误是 不应该出现的异常情况 发生。
正如规范所述,Error 仅在以下情况下被抛出
很可能,当出现Error 时,应用程序几乎无能为力,并且在某些情况下,Java 虚拟机本身可能处于不稳定状态(例如VirtualMachineError)
虽然Error 是Throwable 的子类,这意味着它可以被try-catch 子句捕获,但它可能不是真正需要的,因为当@ 出现时应用程序将处于异常状态987654335@被JVM抛出。
在Java Language Specification, 2nd Edition 的11.5 The Exception Hierarchy 部分中还有一个关于此主题的简短部分。
【讨论】:
如果您足够疯狂地创建一个新的单元测试框架,您的测试运行程序可能需要捕获任何测试用例抛出的 java.lang.AssertionError。
否则,请参阅其他答案。
【讨论】:
还有其他几种情况,如果您发现错误,您必须重新抛出它。例如ThreadDeath 永远不应该被捕获,如果你在封闭的环境(例如应用程序服务器)中捕获它可能会导致大问题:
应用程序仅在必须清理时才应捕获此类的实例 异步终止后。如果 ThreadDeath 被方法捕获, 重要的是要重新抛出它,这样线程才能真正死掉。
【讨论】:
Errors.
非常非常罕见。
我只为一个非常具体的已知案例做这件事。 例如,如果两个 independence ClassLoader 加载相同的 DLL,则可能会抛出 java.lang.UnsatisfiedLinkError。 (我同意我应该将 JAR 移动到共享类加载器)
但最常见的情况是您需要登录才能知道用户投诉时发生了什么。您希望向用户发送消息或弹出窗口,而不是默默地死掉。
即使是 C/C++ 程序员,他们也会在退出前弹出错误并告诉人们不理解的内容(例如内存故障)。
【讨论】:
在一个 Android 应用程序中,我遇到了 java.lang.VerifyError。我正在使用的库无法在具有旧版本操作系统的设备中运行,并且库代码将引发此类错误。我当然可以通过在运行时检查操作系统的版本来避免错误,但是:
【讨论】:
在测试环境中捕获 java.lang.AssertionError 非常方便...
【讨论】:
理想情况下,我们不应该处理/捕获错误。但是根据框架或应用程序的要求,可能存在我们需要做的情况。假设我有一个 XML Parser 守护进程,它实现了消耗更多内存的 DOM Parser。如果有一个要求,比如 Parser 线程在得到 OutOfMemoryError 时不应该死掉,而是应该处理它并向应用程序/框架的管理员发送消息/邮件。
【讨论】:
理想情况下,我们不应该在 Java 应用程序中捕获 Error,因为它是一种异常情况。应用程序将处于异常状态,并可能导致乱码或给出一些严重错误的结果。
【讨论】:
在检查断言的单元测试中捕获错误可能是合适的。如果有人禁用断言或以其他方式删除您想知道的断言
【讨论】:
当 JVM 不再按预期工作或即将运行时,会出现错误。如果你捕捉到一个错误,并不能保证catch块会运行,更不能保证它会运行到最后。
还要看运行的电脑,当前的内存状态,所以没办法测试,尽力而为吧。你只会有一个危险的结果。
您还将降低代码的可读性。
【讨论】: