Java自定义异常应控制在两层:1个统一基类(如BusinessException)+若干按核心业务实体命名的子类(如OrderNotFoundException),子类直接继承基类、互不继承;用接口(如ErrorCodeAware)替代多层继承以共用能力;命名须体现业务失败语义,避免技术细节与泛化表述。

Java 中自定义异常的层级结构不该追求“全面覆盖”,而应聚焦业务语义清晰、捕获逻辑可控、维护成本低。过度分层反而让 catch 失去意义,也让开发者不敢新增异常——怕一加就打破继承链。
控制在两层以内:顶层基类 + 少量领域子类
推荐采用「1 个统一基类 + 按核心业务实体划分的子类」结构,不建议超过两层:
-
顶层基类(如
BusinessException):封装errorCode、message、cause和可选的context(Map),实现Serializable,提供全参构造器 -
子类只表达一种明确失败语义:例如
OrderNotFoundException、PaymentTimeoutException、InventoryInsufficientException,每个都直接继承自顶层基类,彼此之间不继承 - 拒绝三层及以上结构(如
BusinessException → OrderException → OrderAlreadyShippedException),这类设计容易退化为“为分类而分类”,实际捕获时难以区分优先级,也违背“异常是失败场景而非技术路径”的设计原则
用接口代替中间抽象层
当多个异常需要共用能力(如携带错误码、支持日志上下文、可序列化),不要靠继承拉平,而是提取为接口:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 定义
ErrorCodeAware接口,含getErrorCode()方法,让所有异常实现它 - 定义
ContextAware接口,支持附加结构化字段(如orderId、skuId) - 这样既保持类结构扁平,又支持统一处理逻辑(如全局处理器按
errorCode映射 HTTP 状态码)
命名体现“谁在哪种场景下因何失败”,不体现技术路径
异常名是第一道排查线索,必须直击业务本质:
- ✅ 好名字:
OrderAlreadyShippedException、UserBalanceInsufficientException - ❌ 坏名字:
UpdateOrderStatusException(没说失败原因)、OrderServiceException(泛化无区分度)、DatabaseQueryFailedException(暴露技术细节,应包装为业务语义) - 避免为每个错误码新建一个异常类。同一语义下的不同错误码(如“订单不存在”有多个原因),用同一个异常类 + 不同
errorCode即可
所有子类必须提供完整构造器,且禁止重写关键方法
构造器缺失或不当会直接导致日志断链、前端无法识别、跨服务反序列化失败:
- 每个子类至少提供两个构造器:
XXXException(String msg)和XXXException(String msg, Throwable cause) - 务必调用父类
super(msg, cause),确保异常链完整;禁止只留单参String构造器 - 不要重写
getMessage()或getCause(),尤其不能返回固定字符串——这会让原始堆栈和根因丢失,调试时只剩“系统繁忙,请稍后再试”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










