955 字
约 3 分钟
6
S04-B07 · 异常处理:分类、最佳实践与常见反模式

适用读者:写 catch (Exception e) {} 的人。
前置:S04-B05。
实测环境:OpenJDK 21.0.12.1(2026-11-05)。
覆盖旧文:A-1711(173) A-1713(168) A-2603(353) A-1712(117) A-1715(69) = 880 点。
覆盖扩展:E-S04-008(受检 vs 非受检的取舍)。
上一篇:S04-B06 · 接口、抽象类、Lambda 与枚举
下一篇:S04-B08 · 字符串与常量池。


1. 一句话结论

异常分两类:受检(编译期强制处理,表示"可预期的外部失败")与非受检(运行期,表示"程序 bug")。处理原则只有一条:能恢复就处理,不能恢复就抛出并带上上下文。


2. 实测:三个最容易搞错的细节

static int finallyReturn() { try { return 1; } finally { return 2; } }

实测输出:

finally 里的 return 覆盖: 2
使用资源
关闭资源
捕获 NPE: Cannot invoke "String.length()" because "<local0>" is null
外层捕获: 内层
现象 结论
finally 里的 return 会覆盖 try 里的返回值(且吞掉异常)→ 强烈不推荐
try-with-resources 自动调用 close(),顺序:资源关闭在离开块时
NPE 消息 JDK 14+ 的"有帮助的 NPE"会指出具体调用

3. 异常分类(E-S04-008)

类型 例子 是否强制处理 语义
Error OutOfMemoryError、StackOverflowError ❌ 不该捕获(JVM 级问题)
受检 Exception IOException、SQLException ✅ 可预期的外部失败
非受检 RuntimeException NullPointerException、IllegalArgumentException ❌ 编程错误

判据(受检 or 非受检):

调用方能否合理地恢复? 用哪种
能(重试、换路径、提示用户) 受检异常
不能(参数非法、状态错乱) 非受检

4. 五个反模式

反模式 问题 正确做法
catch (Exception e) {} 吞异常,问题消失无踪 至少记录日志并保留原异常
catch 后 printStackTrace() 生产环境看不到 用日志框架 + 上下文
throw new RuntimeException(e.getMessage()) 丢失原始栈 new RuntimeException("上下文", e)
用异常做流程控制 性能与可读性都差 用返回值/Optional
finally 里 return/throw 覆盖返回值、吞异常 只在 finally 里释放资源

"丢失原始栈"这一条最致命:e.getMessage() 只有一句话,没有堆栈,线上无法定位。


5. try-with-resources(A-1715)

try (InputStream in = open(); OutputStream out = create()) {
    // 使用
}   // 自动 close,顺序与声明相反
对比 传统 try-finally try-with-resources
代码量 多 少
异常抑制 手动处理 自动 addSuppressed
漏关风险 高 低

判据:任何实现了 AutoCloseable 的资源,都用 try-with-resources。


6. 自定义异常的规范

要点 说明
继承合适的父类 受检失败 → Exception;编程错误 → RuntimeException
带上下文 至少 message + 关键 id
保留 cause 构造器传 Throwable cause
不要过度细分 每个场景一个异常类会导致 catch 泛滥

7. 自检

  1. finally 里 return 会怎样?为什么不推荐?
  2. 受检与非受检的选择判据是什么?
  3. throw new RuntimeException(e.getMessage()) 丢了什么?
  4. try-with-resources 相比 try-finally 的两个优势?
  5. 什么时候该自定义异常?

8. 下一篇

S04-B08 · 字符串、常量池与编码

异常讲完,基础层最后一篇:字符串为什么不可变、常量池与 intern、以及"拼接 20000 次慢 189 倍"的实测。


本篇属于系列《S04 · Java 语言核心》。旧文来源:A-1711/1713/2603/1712/1715;扩展点 E-S04-008。输出为 OpenJDK 21 实测。

S04-B07 · 异常处理:分类、最佳实践与常见反模式
https://www.clxhxhhr.top/posts/5222/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。