应严格控制自定义异常类数量,仅在具备唯一业务语义、需差异化处理、须携带结构化字段或被多模块异构依赖时新增;推荐用ErrorCode枚举+BusinessException统一管理,继承RuntimeException为主,所有异常须提供完整构造方法并建立代码检查机制。

避免自定义异常类过度膨胀,核心不是“少写几个类”,而是让每个异常类都真正承担不可替代的职责。多数项目里泛滥的异常类,其实只是语义重复、结构雷同、使用场景模糊的“影子类”。
只在四个明确场景下新建异常类
新增一个异常类前,必须能回答清楚以下任一问题:
- 这个错误有没有唯一且不可被标准异常覆盖的业务语义?比如
InsufficientBalanceException比IllegalArgumentException更准确表达“钱不够”,而不是“参数不对” - 下游是否需要按类型做差异化处理?例如支付失败要重试,风控拒绝要告警,两者不能共用一个 catch 分支
- 是否必须携带结构化字段(如 error code、order id、traceId),且这些信息无法通过 message 或 cause 合理传递?
- 这个异常是否被多个模块依赖,且各模块对它的响应逻辑不同(如网关需返回 HTTP 400,日志需脱敏,监控需打标)?
用枚举 + 通用异常替代一堆具体类
别为每个错误码建一个类。推荐做法:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 定义一个
BusinessException,含errorCode、message、details字段 - 所有业务错误统一用
ErrorCode枚举管理:每个枚举项自带默认提示、HTTP 状态码、是否可重试、是否需审计等元信息 - 抛出时只需
throw new BusinessException(ErrorCode.ORDER_NOT_FOUND) - 捕获方通过
exception.getErrorCode()判断分支,无需 import 大量异常类
继承设计要克制,优先选 RuntimeException
除非你明确要求调用方必须处理,否则不要继承 Exception:
立即学习“Java免费学习笔记(深入)”;
-
业务异常(如余额不足、状态非法)→ 继承
RuntimeException,调用方按需捕获,不强制打断代码流 -
系统级异常(如 DB 连接中断、配置加载失败)→ 继承
Exception,强制上层决策恢复策略 - 所有自定义异常必须提供
String和String + Throwable构造方法,确保异常链不丢根因 - 禁止空构造、禁止无参 throw,消息必须具体可读,不能是 "error 1001"
建立机制防止回退
单靠约定容易失效,得有检查手段:
- 代码评审清单中加入:“新增异常类是否满足必要条件?请说明不可替代性”
- 用 SonarQube 或 Checkstyle 限制命名空间(如只允许
exception包下)、禁止模糊命名(如XXXException2) - 定期扫描所有
*Exception.java,标记仅含空构造、无字段、无额外方法的“僵尸类”,列入清理计划

















