应根据异常是否需调用方主动应对来选择:可恢复的外部环境问题用受检异常(如ioexception),编程错误或非法状态用非受检异常(如nullpointerexception),自定义异常依职责决定继承exception或runtimeexception。

选择受检还是非受检异常,核心看这个异常是否该由调用方主动应对——能且应该处理,就用受检;属于代码缺陷或调用方无能为力,就用非受检。
看异常的成因和可恢复性
受检异常适合外部环境引发、程序本身无法控制但可恢复的问题。比如读文件时磁盘损坏、数据库连接超时、网络请求失败。这类问题可能只在特定环境下出现,调用方可以重试、降级、提示用户或切换备用路径。
- 典型例子:IOException、SQLException、InterruptedException
- 关键判断:如果去掉 try-catch 或 throws,编译直接报错,说明它被设计为“你得看见并做点什么”
看是否属于编程错误或内部状态非法
非受检异常(即 RuntimeException 子类)适用于逻辑错误、参数校验失败、状态不一致等本不该发生的场景。它们反映的是代码写错了,或者输入/状态没按约定来,应靠预防而非捕获。
- 典型例子:NullPointerException、IllegalArgumentException、IllegalStateException、ArrayIndexOutOfBoundsException
- 常见做法:方法开头用 if + IllegalArgumentException 快速失败,不污染接口签名
看调用方有没有处理能力与责任
不是所有“能 catch”的异常都该被强制处理。如果上层业务逻辑根本无法从 SQLException 中恢复,硬加 throws 只会让 API 难用,还催生大量空 catch 或 log 后吞掉的反模式。
- 推荐做法:在服务入口或适配层,把第三方库抛出的受检异常转为非受检(如 new RuntimeException(e)),避免污染核心业务流
- 特别注意:Spring @Transactional 默认只对 RuntimeException 和 Error 回滚,吞掉受检异常会导致事务失效
自定义异常时的继承选择
继承 Exception 还是 RuntimeException,取决于你希望调用方承担什么职责:
- 想强制调用方决策(如支付失败必须重试或跳转失败页)→ 继承 Exception(受检)
- 只是校验不通过(如订单状态非法、金额为负)→ 继承 IllegalArgumentException 等运行时异常(非受检)
- 切忌为图省事把本该受检的业务异常改成 RuntimeException——这等于把“系统暂时不可用”说成“程序写崩了”











