Java中自定义异常与标准异常应分层协作:标准异常处理通用问题,自定义异常承载业务语义;关键依据是调用方是否需感知并决策——能用IllegalArgumentException、IllegalStateException等标准异常精准表达的,优先复用;需表达业务规则、绑定错误码、支持统一拦截响应时,才应定义如InsufficientBalanceException等自定义异常,并按语义选择继承Exception或RuntimeException。

Java 中自定义异常和标准异常不是“选一个用”,而是分层协作:标准异常处理通用问题,自定义异常承载业务语义。关键在“该不该让调用方感知并决策”——能用标准异常说清的,就别造新类;需要表达业务规则、错误码、可恢复动作的,才自定义。
优先复用标准异常的场景
多数参数校验、状态约束、边界问题,JDK 已提供精准语义的异常类型,直接用更清晰、更轻量:
- IllegalArgumentException:传入非法值,比如金额为负、页码小于 1、邮箱格式错误
- IllegalStateException:对象处于不合法状态时被调用,如未登录访问用户中心、订单已关闭却尝试支付
- NullPointerException:明确禁止 null 参数时(比用 IllegalArgumentException 更符合约定)
- IndexOutOfBoundsException:集合或数组索引越界,比手动 throw new RuntimeException("index out") 更规范
必须自定义异常的典型情况
当错误含义超出标准异常覆盖范围,且需支持统一处理、日志追踪、前端友好提示时,应定义自定义异常:
- 涉及业务规则且需调用方主动响应:例如
InsufficientBalanceException(余额不足)、OrderStatusConflictException(状态冲突),这类异常往往带订单号、用户 ID、当前/期望状态等上下文字段 - 需要绑定错误码与国际化消息:如
BusinessException(ErrorCode.ORDER_NOT_FOUND, "订单不存在"),便于网关统一映射 HTTP 状态码和前端提示文案 - 需全局拦截做特殊逻辑:比如对所有
PermissionDeniedException自动记录审计日志,或对RateLimitExceededException返回限流头信息
自定义异常的继承策略要匹配语义
不是“统一继承 RuntimeException 就完事”,而要看是否需要编译期强制处理:
- 调用方不处理就可能引发数据不一致或流程错乱 → 继承 Exception(受检异常)
例如:调用支付接口返回“库存扣减失败”,上游必须重试或降级,此时用InventoryDeductionFailedException extends Exception - 本质是代码缺陷或非法输入,修复靠改逻辑而非业务兜底 → 继承 RuntimeException
例如:DTO 字段校验失败、配置缺失导致初始化异常、数据库查不到却硬取属性 —— 这些应在测试或启动阶段暴露,不鼓励运行时捕获
避免常见失衡做法
失衡往往表现为两种极端:
- 过度自定义:为每个 HTTP 状态码(400/401/403/404)都建一个异常类,导致包里堆满
BadRequestExceptionUnauthorizedExceptionForbiddenException……其实用一个ClientException+ 错误码字段就能覆盖 - 拒绝自定义:所有业务错误全扔
RuntimeException或IllegalArgumentException,结果日志里全是“非法参数”,根本分不清是用户输错手机号,还是风控服务超时返回空,还是 DB 主键冲突
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











