受检异常与非受检异常如何设计?finally 的规则是什么?
结论先行:受检异常(checked)继承 Exception 但不属于 RuntimeException,编译器强制捕获或声明 throws,代表可预期、应恢复的错误(如 IOException);非受检异常(unchecked)继承 RuntimeException 或 Error,不强制处理,代表程序缺陷或不可恢复的问题(如 NPE、OOM)。finally 块在正常结束、return 前、异常抛出时都会执行,唯一例外是 JVM 退出或线程被强杀。
设计建议
| 场景 | 推荐类型 | 示例 |
|---|---|---|
| 外部、可恢复的错误 | 受检异常 | 文件不存在、网络超时 |
| 参数/状态错误 | 非受检异常 | IllegalArgumentException、NullPointerException |
| 框架与底层逻辑错误 | 非受检异常 | IllegalStateException |
finally 三条铁律
- finally 里不要 return:它会覆盖 try/catch 的返回值,甚至吞掉异常。
- try 或 catch 中先 return 时,finally 仍会先执行完毕才真正返回。
- finally 中若抛出新异常,会替换原来的异常(资源关闭场景常用 try-with-resources 规避)。
import java.io.*;
public class FinallyDemo {
static int test() {
try {
return 1; // 先执行 finally 再返回
} finally {
System.out.println("finally 一定执行");
// 若这里写 return 2,会覆盖上面的 1 —— 反模式
}
}
static void read() throws IOException { // 受检异常必须声明或捕获
InputStream in = new FileInputStream("x.txt");
try {
in.read();
} finally {
in.close(); // 此时 close 抛错会掩盖原异常,JDK7+ 应改用 try-with-resources
}
}
}
常见追问 / 记忆点
- 记忆点:受检 = 该处理的外部错误,非受检 = 有 Bug 或不可恢复;finally 别 return、别抛新异常。
- 追问:Spring 声明式事务默认只对 RuntimeException 与 Error 回滚,受检异常默认不触发回滚。
- 追问:finally 中 System.exit、JVM Crash、守护线程中断等极少数场景可能不执行。