受检异常用于调用方能预期且可恢复的外部失败,如远程配置加载失败、支付回调超时;非受检异常用于程序缺陷或业务规则违例,如ID为空查用户,应通过校验拦截而非捕获处理。

企业级开发中,受检异常与非受检异常的使用边界不是由语法决定的,而是由“谁该处理”和“能否恢复”这两个实际问题定义的。简单说:能预期、可干预的外部失败用受检;逻辑错、状态违、业务规则不满足统一用非受检。
按异常性质划分责任归属
受检异常适用于调用方确实有办法补救的场景,它强制提醒开发者“这事可能出问题,你得想好怎么办”。比如:
- 远程配置加载失败 → 上层可切本地缓存或返回默认值
- 第三方支付回调超时 → 可触发人工对账或延迟重试
- 文件上传临时IO中断 → 前端提示用户“网络波动,请重试”
而非受检异常对应的是程序自身缺陷或业务流程中的明确分支,不该靠 catch 挽救,而应靠校验提前拦截:
- ID 为空就查用户 → 抛 IllegalArgumentException
分层封装,屏蔽技术细节
底层技术异常(如 SQLException、IOException)本身是受检的,但绝不该穿透到业务层。必须在边界处做一次语义化包装:
立即学习“Java免费学习笔记(深入)”;
- DAO 层捕获 SQLException → 转为 DataAccessException 或 DbConnectionException
- HTTP 客户端抛 IOException → 包装成 RemoteServiceUnavailableException
- 所有包装都需传入原始异常作为 cause:new BusinessException("下单失败", e)
这样既保留根因便于排查,又让上层只关注业务含义,不被 JDBC 或 HTTP 细节干扰。
统一用错误码驱动业务异常
库存不足、权限拒绝、余额不够这类情况,本质是业务规则判断结果,不是意外故障。推荐做法是:
- 定义统一的 BusinessException 基类,内置 error code、message、traceId
- 避免 throw new RuntimeException("余额不足") 这种无结构写法
- 配合 @ControllerAdvice 全局拦截,输出标准 JSON:{"code":"BALANCE_INSUFFICIENT","msg":"账户余额不足"}
前端或调用方靠 code 做差异化处理,而不是靠 try-catch 判断字符串内容。
谨慎保留受检异常的少数场景
并不是完全不用受检异常。以下情况仍值得保留其强制语义:
- 对外 SDK 接口,要求调用方必须处理证书过期、配额耗尽等强契约失败
- 核心金融交易中,某些网关确认失败必须走人工复核,不能静默忽略
- 遗留系统桥接层,协议约定必须返回特定受检异常类型,且无法升级调用方
其余绝大多数内部服务间调用,都应以非受检异常为主流,靠统一异常处理器兜底,重点保证语义清晰、可观测、易定位。


















