Error代表程序应终止的系统级故障,不可恢复;Exception分checked(编译强制处理)和unchecked(运行时逻辑错误),需依业务可恢复性决定继承关系。

Throwable体系里,Error和Exception根本不是同一类问题
它们同属 Throwable,但语义和处理意图完全割裂:一个代表“程序该停了”,另一个代表“程序还能救”。JVM 设计上就禁止你对大多数 Error 做有意义的捕获——不是语法不允许,而是逻辑上没意义。
捕获 OutOfMemoryError 是典型误区
有人写 try-catch (OutOfMemoryError e) 试图兜底,结果发现要么不触发,要么触发时连日志都打不出来。原因很直接:OutOfMemoryError 发生时,JVM 已经没有足够堆内存分配给新对象(包括 String、Exception 实例甚至日志框架的内部缓冲区),catch 块本身可能就执行不了。
- 真正可行的做法是用
-XX:+HeapDumpOnOutOfMemoryError让 JVM 自动 dump 内存快照,再用jhat或 VisualVM 分析 - 监控层面应提前用
MemoryUsage+NotificationListener在内存使用率达 85% 时告警,而不是等 Error 抛出 -
StackOverflowError同理:递归过深时栈帧已满,连构造异常对象的空间都没有,catch 几乎必然失效
Exception 分两类,编译器强制你区分对待
Java 编译器只对 checked Exception(比如 IOException、SQLException)做语法检查:不 try-catch 或不声明 throws,直接编译失败。而 RuntimeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)属于 unchecked,编译期放行,全靠你代码逻辑保障。
-
FileNotFoundException是 checked:文件是否存在在运行前无法 100% 确定,必须显式处理 -
IllegalArgumentException是 unchecked:这是你 API 的契约问题,应该在调用前校验参数,而非靠 catch 补救 - 滥用
catch (Exception e)会吞掉本该暴露的 unchecked 异常,掩盖逻辑缺陷
自定义异常该继承 Exception 还是 RuntimeException?
取决于它是否属于“调用方能预见且有责任处理”的场景。比如支付接口返回余额不足,这是业务可预期的分支,应定义为 InsufficientBalanceException extends Exception;而数据库连接池耗尽导致的 SQLException 包装异常,若上层无法重试或降级,就不该包装成 checked,否则徒增 try-catch 噪声。
- 继承
Exception→ 调用方必须处理,适合外部依赖失败、用户输入非法等明确需响应的场景 - 继承
RuntimeException→ 调用方无需强制处理,适合内部状态不一致、配置加载失败等应尽快暴露的问题 - 永远不要继承
Error:你不是 JVM 开发者,也不该模拟系统级崩溃
throw 或 catch 时,问自己一句:这个异常发生后,调用方除了打印日志,还能做什么?如果答案是“什么都做不了”,那它大概率不该是 Exception。

















