受检异常混淆业务分支与技术错误,迫使正常流程嵌入try-catch,破坏可读性、抽象性与函数式编程支持。

受检异常常被诟病,核心在于它把业务逻辑判断强行塞进异常流程,让代码读起来像在排查故障,而不是在表达业务规则。
混淆“错误”与“分支”
可预期的业务结果(如“库存不足”“手机号已注册”)本该用 if 判断 + 明确返回值表达,但一旦定义为受检异常,调用方就必须写 try-catch 或 throws。这导致:
- 正常流程被异常块切割,主干逻辑被淹没在 catch 块里
- 阅读代码时需反复跳转,无法一眼看出“什么条件下走哪条路”
- 同一个方法里混着技术异常(如文件读取失败)和业务拒绝(如参数校验不通过),语义模糊
污染接口契约
方法签名被迫暴露底层实现细节,破坏分层抽象:
- public Order createOrder(...) throws SQLException, IOException —— 调用方不该关心你是查数据库还是读配置文件
- 上层服务因底层变更被迫改签名,哪怕业务语义完全没变
- API 文档充斥技术异常类型,掩盖真实业务约束(比如“支付失败”实际只对应“余额不足”,却列了七八种 SQLException)
催生无效防御性代码
为满足编译要求,开发者常写出无实质处理的样板代码:
- 空 catch 块:catch (Exception e) { } —— 错误静默丢失
- 日志仅 printStackTrace(),未接入监控或告警体系
- 层层包装再抛:throws new RuntimeException(e) —— 编译检查形同虚设,还丢掉原始类型信息
干扰函数式与链式调用
Stream、Optional、CompletableFuture 等现代 API 的函数参数不支持 throws 声明:
- list.stream().map(s -> Files.readString(Path.of(s))) 直接编译失败
- 不得不额外封装工具方法,或改用 Optional.empty() 等替代方案,反而增加理解成本
- 业务逻辑被迫退回到传统 for 循环,放弃声明式表达优势











