java自定义异常需以exception结尾、用“形容词+名词”命名(如usernotfoundexception),按业务域分包(如com.example.app.user.exception),提供四个标准构造器并声明serialversionuid,避免通用基类。

Java 中自定义异常类的命名与设计,核心是让异常“一读就懂、一捕就准、一查就明”。不是堆功能,而是靠语义清晰、结构合理、契约明确来支撑长期可维护性。
命名必须以 Exception 结尾,用“形容词+名词”表达业务语义
这是 Java 社区硬性约定,IDE、静态检查工具(如 SonarQube)、Spring 的 @ExceptionHandler 都依赖这个后缀识别异常类型。不加 Exception,哪怕继承了 RuntimeException,也会被当成普通类处理。
- ✅ 推荐写法:UserNotFoundException、InsufficientBalanceException、InvalidOrderStatusException
- ❌ 避免写法:MyException(无业务含义)、OrderFail(缺后缀)、ValidateFailedException(动词开头,不符合习惯)、InvldInputExc(缩写难懂)
- 优先把领域对象前置:用户相关错用 UserLockedException,而不是泛泛的 ResourceLockedException;支付失败用 PaymentTimeoutException,而非 ServiceException
包结构按业务域组织,不塞进通用 exception 包
异常类要和它服务的模块在一起,而不是全扔进 com.example.app.exception 这种大杂烩包里。这样既符合语义归属,也利于模块拆分和 IDE 导入时避免同名冲突。
- 用户模块的异常放在:com.example.app.user.exception
- 订单模块的异常放在:com.example.app.order.exception
- 支付模块的异常放在:com.example.app.payment.exception
- 好处很明显:新人看代码能快速定位异常来源;Maven 拆模块时,异常随业务逻辑天然复用;日志或监控按包路径聚合分析也更精准
构造器必须完整,serialVersionUID 不可省略
框架内部(如 Spring MVC 参数解析、MyBatis SQL 执行)常隐式调用带 message 和 cause 的构造器。少一个,上线后可能因 NoSuchMethodError 崩溃。
- 必须提供四个标准构造器:
public XxxException()
public XxxException(String message)
public XxxException(Throwable cause)
public XxxException(String message, Throwable cause) - 如需携带业务字段(如 orderId、errorCode),可额外增加构造器,但不能删减上述四个
- 只要异常可能被序列化(网络传输、日志落盘、跨 JVM 链路追踪),就必须显式声明:
private static final long serialVersionUID = 1L;
字段增删或类型变更时,记得同步更新该值(如改为 2L)
慎用通用基类,按错误语义独立建类
别搞一个 MyBusinessException 扛下所有业务错误。这等于放弃 Java 类型系统的编译期检查能力,也让捕获、日志、告警失去分类依据。
- 调用方无法写 catch (InsufficientBalanceException e) 做针对性处理
- 方法签名若只写 throws MyBusinessException,等同于 throws Exception,契约失效
- 同一模块内语义相近的错误(如多种参数校验失败),可用一个异常类,靠 message 或 errorCode 区分;跨模块或本质不同的错误(如库存不足 vs 订单超时),必须分设类名和包路径
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











