
Exception 能 catch,Error 不该 catch
不是一回事。它们都继承自 Throwable,但语义和用途截然不同:程序该处理的是 Exception,而 Error 是 JVM 自身崩溃或资源彻底耗尽的信号,比如 OutOfMemoryError 或 StackOverflowError —— 这类错误发生时,JVM 可能已处于不一致状态,强行捕获并“继续执行”往往掩盖问题、引发更诡异的行为。
- 捕获
Exception是常规防御手段:比如读文件前 catchFileNotFoundException,网络请求后处理IOException - 捕获
Error属于反模式:你无法可靠地“恢复”一个栈溢出,也很难在内存已耗尽时安全地释放资源 - 极少数例外场景(如监控 Agent、容器化沙箱)可能 try-catch
Throwable做兜底日志,但绝不能吞掉、也不能尝试“重试”或“降级”
编译器强制你管 Exception,但对 Error 完全放任
Java 编译器只强制处理“受检异常”(Checked Exception),即非 RuntimeException 的 Exception 子类;而所有 Error 都是“非受检”的,编译器连提醒都不会给。
- 写
FileInputStream fis = new FileInputStream("a.txt");会报错:必须用try-catch或加throws IOException - 但写
int[] arr = new int[Integer.MAX_VALUE];却不会编译报错,运行时抛OutOfMemoryError就直接崩 - 这种设计不是疏忽,而是明确告诉你:
Error不在程序逻辑可控范围内,别指望靠编译检查来兜底
RuntimeException 是 Exception,但和 Error 一样“不用强制处理”
容易混淆的一点:NullPointerException、ArrayIndexOutOfBoundsException 这些常见崩溃,属于 RuntimeException(即“非受检异常”),和 Error 一样不强制捕获 —— 但它们的性质完全不同。
-
RuntimeException是程序 bug 或输入校验缺失导致的,理应通过修复逻辑、加 null 检查、单元测试来消灭,而不是靠 catch -
Error是 JVM 底层失能,比如 GC 已失败多次、线程栈撑爆、本地内存分配失败,程序代码再严谨也无济于事 - 所以看到
RuntimeException要改代码;看到OutOfMemoryError要调 JVM 参数、查内存泄漏、看堆 dump —— 解决路径完全不在一个层面
线上服务里最常误用 Error 的地方
有人为了“防止服务挂掉”,在顶层 filter 或全局异常处理器里把 Throwable 全部 catch,并返回 500 页面或打日志后继续运行。这在绝大多数生产环境是危险操作。
立即学习“Java免费学习笔记(深入)”;
-
LinkageError(如NoClassDefFoundError)可能意味着类加载器状态混乱,后续任意类加载都可能失败 -
VirtualMachineError子类(包括OutOfMemoryError)发生后,JVM 内部结构可能已损坏,此时对象 finalize、锁释放、线程中断等行为都不可信 - 真正稳妥的做法是:记录完整堆栈 + 触发 JVM 快速退出(如
System.exit(1))+ 由进程管理器(如 systemd、K8s)拉起新实例
复杂点在于:有些框架(如 Spring Boot)默认会把 OutOfMemoryError 当作普通异常拦截并返回 HTTP 500,而你根本没意识到服务其实在“带伤跑”。得主动配置 -XX:+ExitOnOutOfMemoryError 或监听 java.lang.OutOfMemoryError 信号做快速熔断。










