自定义异常用于补充jdk内置异常,承载业务语义并支持差异化处理;应继承runtimeexception以适配web快速失败风格,添加错误码、嵌套原因和上下文信息,并配合@controlleradvice统一拦截。

自定义异常不是为了“替代”JDK 内置异常,而是在合适的地方补充它。JDK 异常负责通用错误(如空指针、IO 失败、数组越界),而业务逻辑中的特定问题——比如“用户余额不足”“订单状态非法”“手机号格式不合规”——用内置异常表达不清、捕获难区分、处理难统一。这时候,自定义异常才真正发挥作用。
明确区分使用场景:什么该用自定义,什么该留内置
不必也不该把所有异常都换成自定义类。关键看两点:是否携带业务语义、是否需要差异化处理。
- 遇到
NullPointerException或ArrayIndexOutOfBoundsException,说明代码有 bug,应修复逻辑,而不是包装成自定义异常 - 调用
FileInputStream报FileNotFoundException,这是标准 IO 异常,保留即可;但若业务要求“配置文件缺失时需触发告警并降级”,可封装一层自定义异常来承载该策略 - 用户登录时密码错误,
IllegalArgumentException虽能用,但无法体现“认证失败”这个业务意图;换成AuthenticationFailureException,调用方一眼明白含义,也方便统一拦截返回 JSON 错误码
继承选择要匹配语义和管控需求
继承关系决定异常是否强制处理,直接影响 API 设计和调用方行为。
- 继承
RuntimeException:适用于业务校验类异常(如参数非法、状态冲突),调用方无需try-catch或throws,符合多数 Web 场景快速失败、统一全局异常处理器的风格 - 继承
Exception:仅在极少数强契约场景下使用,例如银行核心系统中“转账失败必须显式声明并重试”,这时让编译器强制调用方处理,避免遗漏 - 绝不继承
Error:它是 JVM 层严重故障(如OutOfMemoryError),程序无法恢复,也不应由业务代码构造或抛出
通过结构增强表达力,让异常自带上下文
一个好用的自定义异常,不止是换个名字,还要带出关键信息,减少日志补全和调试成本。
- 添加业务错误码字段(如
private final int code = 400102;),便于前端识别、监控平台聚合 - 支持嵌套原因(
super(message, cause)),比如数据库操作失败时,把原始SQLException作为 cause 传入,保留完整调用链 - 重写
toString()或提供toLogString()方法,自动拼接 userId、orderId、timestamp 等现场数据,避免每个 catch 块手动打点
配合统一异常处理机制才能真正落地
光有自定义异常类不够,必须搭配上层拦截策略,否则容易散落在各处 catch 中,失去意义。
- Spring 项目推荐用
@ControllerAdvice + @ExceptionHandler全局捕获自定义异常,统一转为 HTTP 状态码和标准响应体 - 避免在 service 层大量
try-catch自定义异常再抛新异常,这会模糊错误源头;应让异常自然向上冒泡,由最外层统一消化 - 日志框架(如 Logback)可配置 MDC,在异常抛出前注入 traceId、userId,使自定义异常日志天然可追溯
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











