Java自定义异常需分层设计:继承Exception实现检查型异常(如InsufficientStockException)用于必须处理的业务异常;继承RuntimeException实现非检查型异常(如InvalidOrderStatusException)用于可选捕获的逻辑错误;推荐构建异常继承树并配套统一处理器与日志规范。

Java 中通过继承 Exception 或其子类(如 RuntimeException)来构建自定义异常体系,核心在于按业务语义分层设计、明确检查型/非检查型异常的使用场景,并配合有意义的构造器和错误码机制。
继承 Exception 实现检查型自定义异常
适用于必须显式处理、影响主流程的业务异常,比如“库存不足”“用户未登录”。这类异常继承 Exception,编译器强制调用方捕获或声明抛出。
示例:
public class InsufficientStockException extends Exception {
private final int errorCode = 1001;
public InsufficientStockException(String message) {
super(message);
}
public InsufficientStockException(String message, Throwable cause) {
super(message, cause);
}
public int getErrorCode() {
return errorCode;
}
}
- 构造器保留标准重载(带 message 和 cause),便于链式异常追踪
- 可附加业务字段(如
errorCode、timestamp),避免后续靠字符串解析提取信息 - 不建议在自定义异常中重写
printStackTrace()等底层行为,保持语义清晰
继承 RuntimeException 实现非检查型异常
适用于程序逻辑错误或无需强制处理的业务异常,例如“参数非法”“状态冲突”。继承 RuntimeException 后,调用方可选择性捕获,更贴近现代 API 设计习惯(如 Spring 的 DataAccessException 体系)。
立即学习“Java免费学习笔记(深入)”;
示例:
public class InvalidOrderStatusException extends RuntimeException {
private final String orderId;
private final String currentStatus;
public InvalidOrderStatusException(String orderId, String currentStatus) {
super("Order " + orderId + " cannot transition from status: " + currentStatus);
this.orderId = orderId;
this.currentStatus = currentStatus;
}
// 提供 getter 方便日志或监控提取关键字段
public String getOrderId() { return orderId; }
public String getCurrentStatus() { return currentStatus; }
}
- 把关键上下文(如订单号、状态)作为字段保存,比拼接进 message 更利于结构化日志和告警
- 避免滥用非检查型异常掩盖本应校验前置的错误(如空指针本该用 Optional 或判空)
- 与 Spring 等框架集成时,常配合
@ControllerAdvice统一转换为 HTTP 响应
构建异常继承树,体现业务分层
单一异常类难以覆盖复杂系统。推荐按领域划分父异常,再派生具体子类,形成可扩展的层级结构。
- 顶层定义模块级基类:如
PaymentException extends RuntimeException - 子类聚焦具体问题:如
PaymentTimeoutException、InvalidPaymentMethodException - 父类可统一提供通用方法:如
getTraceId()、toErrorResponse() - 避免过深继承(一般不超过 3 层),否则增加理解和维护成本
配合异常处理器与日志规范
自定义异常的价值在落地时才体现。需配套统一处理策略:
- Web 层用
@ExceptionHandler捕获自定义异常,返回标准化 JSON(含 code、message、details) - 记录异常时优先打印
getCause()和业务字段,而非只打toString() - 敏感信息(如密码、身份证号)不得出现在异常 message 或字段中,必要时脱敏后存入
detailsMap - 对高频异常(如重复提交)考虑限流+降级,而非仅抛异常


















