应根据异常是否需调用方强制处理来选择继承Exception或RuntimeException:需主动干预的业务异常继承Exception,程序缺陷类问题继承RuntimeException。

关键看调用方是否必须处理这个异常,而不是凭感觉或“统一风格”决定。
需要调用方强制响应的场景 —— 继承 Exception
这类异常代表业务中可预期、可恢复、需主动干预的问题。编译器会强制你在调用处加 try-catch 或 throws,避免遗漏关键逻辑。
- 用户余额不足,支付流程必须中断并提示重试或充值
- 订单状态非法(如已取消的订单再次发货),外部系统需明确收到失败信号
- 第三方服务返回特定错误码(如库存扣减失败),上游必须降级或重试
继承 Exception 后,记得提供带业务上下文的构造方法,比如:InsufficientBalanceException(String orderId, BigDecimal balance),方便排查和日志追踪。
属于程序缺陷或不可恢复问题 —— 继承 RuntimeException
这类异常反映代码逻辑问题或运行时环境异常,不期望调用方兜底,而应由开发阶段发现并修复。
立即学习“Java免费学习笔记(深入)”;
- 参数校验失败(如 ID 为空、金额为负),本质是调用方传参错误
- 配置缺失导致 Bean 初始化失败,属于启动阶段应暴露的问题
- 数据库字段映射为空但业务代码强行调用 getter,属于空指针类逻辑漏洞
继承 RuntimeException 不需要在方法签名声明,也不强制捕获,但建议在全局异常处理器中统一记录堆栈和业务标识,便于定位。
别踩这两个常见坑
一是把所有自定义异常都塞进 RuntimeException —— 看似省事,实则掩盖了本该被显式处理的关键业务分支;二是给每个 HTTP 错误码都建一个 Exception 类 —— 大量细粒度异常反而增加维护成本,优先复用标准异常(如 IllegalArgumentException、IllegalStateException)或按语义分层(如 ValidationException、BusinessException)。
一个实用判断口诀
问自己两个问题:
• 这个错误发生后,调用方不处理就可能出错吗?→ 是 → 用 Exception
• 这个错误发生后,改代码才能解决,不是业务决策能绕过的吗?→ 是 → 用 RuntimeException


















