异常体系:受检异常、运行时异常与处理原则
更新时间:2026-09-20。异常是 Java 把「错误处理」从正常流程里分离出来的机制。先看清 Throwable 这棵树,再决定何时 catch、何时抛、要不要受检,线上崩溃分析也依赖这套结构,见 crash/。
一、Throwable 继承树
bash
Throwable
├── Error(JVM 级严重问题,程序通常处理不了)
│ OutOfMemoryError / StackOverflowError / NoClassDefFoundError
└── Exception
├── RuntimeException(非受检,编译不强制)
│ NPE / IllegalArgumentException / IndexOutOfBounds /
│ ConcurrentModificationException ...
└── 其他 Exception(受检 checked,编译期必须处理)
IOException / SQLException / InterruptedException ...| 类别 | 编译器是否强制处理 | 典型成因 | 该不该 catch |
|---|---|---|---|
| Error | 否 | JVM/资源崩溃 | 一般不 catch,让它快速失败 |
| 受检异常 | 是(catch 或 throws) | 外部可恢复故障:IO、网络、SQL | 在能处理处捕获,否则声明抛出 |
| 运行时异常 | 否 | 编程 bug:空指针、越界、非法参数 | 优先修代码而非 catch |
二、三种写法
java
// 1) try-catch-finally:finally 无论是否异常都执行
FileReader r = null;
try {
r = new FileReader("a.txt");
r.read();
} catch (FileNotFoundException e) {
log.warn("文件不存在", e); // 记录根因,别空 catch
} catch (IOException e) { // 先具体后宽泛
log.error("读失败", e);
} finally {
if (r != null) r.close(); // 手动关闭繁琐且易漏
}
// 2) try-with-resources(Java 7+,推荐):AutoCloseable 自动按序关闭
try (FileReader fr = new FileReader("a.txt");
BufferedReader br = new BufferedReader(fr)) {
br.readLine();
} // 自动 close,异常也不会丢(被抑制异常 addSuppressed)
// 3) 声明抛出:本层不处理,交给上层
byte[] load(String p) throws IOException { return Files.readAllBytes(Path.of(p)); }throw 是「抛出一个异常对象」(动作),throws 是「方法签名上声明可能抛哪些受检异常」(契约),两者别混。

三、处理原则
- 不要吞异常:空
catch{}会让 bug 无迹可寻,至少带上下文 log。 - catch 具体类型,不要一上来 catch (Exception/Throwable):否则把 NPE 也一起吞掉。
- 受检异常适度转译:在分层边界把底层
SQLException包成业务异常(throw new BizException("下单失败", e)),保留 cause。 - 优先用 try-with-resources 管理流/连接,杜绝忘记 close。
- 参数非法快速失败:对非法入参主动
throw new IllegalArgumentException,好过带着坏数据跑很久。
坑
- finally 里 return / 改返回值:会覆盖 try 里的返回甚至吞掉异常,finally 只做清理,不要 return。
- catch 顺序反了:先写
catch(Exception)再写具体异常会编译失败(子类异常不可达)。 - catch 里丢失栈:
throw new X(e.getMessage())没传 cause,根因栈断裂,要new X(e)。 - InterruptedException 吞掉不恢复中断状态:捕获后应
Thread.currentThread().interrupt()或继续抛,否则线程池无法正确停任务。 - 把异常当正常控制流:用异常做循环/分支极慢(要填充栈轨迹)且难读,业务判断用 if。
总结
Throwable 分为不该由程序处理的 Error、编译期强制 catch 或 throws 的受检异常(外部可恢复故障),以及不强制、多由编程 bug 引起的 RuntimeException;处理时遵循「能恢复才捕获、捕获要具体且记录根因、资源一律 try-with-resources、跨层转译保留 cause、finally 不 return」,并优先靠修代码与前置校验避免运行时异常,而不是用宽泛 catch 掩盖问题。 JVM 致命错误的现场见 jstack 线程栈。