Exception和Error是Throwable的两个平级子类,前者表示程序应处理的异常,后者表示JVM无法恢复的严重错误;编译器强制处理Checked Exception,对RuntimeException和Error均不检查。

Exception 和 Error 都继承自 Throwable,但角色完全不同
Java 中所有“出问题”的对象都必须是 Throwable 的实例。它只生了两个孩子:Exception 和 Error——它们是平级关系,不是父子关系。这意味着你写 catch (Exception e) 永远抓不到 OutOfMemoryError,因为后者根本不在 Exception 的继承链上。
-
Exception是程序“能且应该”处理的问题:比如文件不存在、网络超时、用户输错格式 -
Error是 JVM 自己扛不住的崩溃信号:比如堆内存彻底耗尽(OutOfMemoryError)、调用栈炸穿(StackOverflowError)、类加载失败(NoClassDefFoundError) - 你 catch
Error不会修复问题,只会掩盖故障征兆;JVM 通常已处于不可恢复状态
编译器强制你管 Exception,但对 Error 和 RuntimeException 睁一只眼
Java 编译器只盯住一类异常:**检查型异常(Checked Exception)**,也就是直接继承 Exception 但不继承 RuntimeException 的那些,比如 IOException、SQLException。你不处理,编译直接报错。
- 必须显式处理:用
try-catch捕获,或用throws声明抛给上层 -
RuntimeException及其子类(如NullPointerException、ArrayIndexOutOfBoundsException)属于**非检查型异常(Unchecked Exception)**,编译器不管,但它们本质仍是Exception -
Error全系都是 unchecked,编译器完全放行——但这不等于你可以放心去 catch 它们
别 catch Error,除非你在做监控埋点
日常开发中,catch (Error e) 几乎总是错误选择。它不会让程序“恢复正常”,反而可能让 JVM 在半死不活的状态下继续执行,引发更诡异的行为。
try {
// 比如触发无限递归
recurseForever();
} catch (StackOverflowError e) {
System.out.println("我抓住了栈溢出!"); // ❌ 危险:此时线程栈已损坏,后续逻辑极可能出错
return; // 甚至 return 都可能失败
}
- 真实场景中,
Error发生时,JVM 往往已无法保证线程安全、资源释放、甚至日志输出的可靠性 - 唯一合理使用
catch (Error)的地方:全局监控(如 APM 工具),仅用于记录日志 + 快速退出,绝不尝试“恢复” - 如果你的代码频繁遇到
OutOfMemoryError,该查堆内存配置、对象泄漏,而不是加个 try-catch
Throwable.getCause() 是定位根因的关键,但 Error 通常没有有效 cause
很多异常是“套娃”出来的:A 异常触发 B 异常,B 再包装成 C。这时 getCause() 能帮你一层层剥开,找到最原始的错误源头。
立即学习“Java免费学习笔记(深入)”;
- 对
Exception(尤其是 checked 类型),框架和库普遍会正确设置 cause,比如SQLException包裹底层IOException - 而
Error多数没有 meaningful 的 cause——OutOfMemoryError就是内存没了,它不会告诉你“是因为哪个 HashMap 没清空” - 所以打印异常时别只看
e.toString(),务必用e.printStackTrace()或e.getCause()循环遍历
OutOfMemoryError 出现在生产环境时,你能否在 5 分钟内判断是内存配小了、还是代码里真有百万级对象没释放——这需要日志、堆快照、GC 日志三者交叉验证,而不是靠 catch 块。










