Java中设计不可变自定义业务异常类,需继承RuntimeException、所有字段声明为final且私有、构造函数完成全部初始化并避免可重写方法调用、通过防御性拷贝或不可变视图防止内部状态被修改。

Java 中设计不可变的自定义业务异常类,核心是让异常对象一旦创建就不能被修改——即所有字段为 final,不提供 setter 方法,构造过程完成全部状态初始化,并避免暴露可变内部对象(如集合、数组、日期等)。
1. 继承合适的异常基类
业务异常通常应继承 RuntimeException(非检查型),除非你明确需要强制调用方处理(此时可选 Exception)。不建议直接继承 Throwable,避免绕过 Java 异常规范。
示例:
public class InsufficientBalanceException extends RuntimeException { ... }
2. 所有字段声明为 final 且私有
异常中若需携带额外业务信息(如错误码、订单 ID、金额等),必须用 final 字段封装,禁止后续修改。
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
常见字段建议包括:
- errorCode:字符串或枚举类型(推荐枚举,更安全)
- businessId:如订单号、用户 ID 等上下文标识
-
details:不可变容器,如
Map.copyOf(...)或ImmutableMap(Guava)
3. 构造函数只接受必要参数并完成完整初始化
不提供无参构造器;每个构造器都应调用 super(message) 并完成所有 final 字段赋值。避免在构造器中调用可重写方法(防止子类破坏不变性)。
示例结构:
public final class PaymentRejectedException extends RuntimeException {
private final String errorCode;
private final String orderId;
private final Map<String, Object> context;
public PaymentRejectedException(String errorCode, String orderId, Map<String, Object> context) {
super(formatMessage(errorCode, orderId));
this.errorCode = Objects.requireNonNull(errorCode);
this.orderId = Objects.requireNonNull(orderId);
this.context = Map.copyOf(Objects.requireNonNull(context));
}
// 只提供 getter,无 setter
public String getErrorCode() { return errorCode; }
public String getOrderId() { return orderId; }
public Map<String, Object> getContext() { return context; }
private static String formatMessage(String code, String id) {
return String.format("Payment rejected: [%s] for order %s", code, id);
}
}
4. 避免暴露可变内部状态
如果字段是数组、Collection、Date、StringBuilder 等可变类型,务必做防御性拷贝或封装为不可变视图:
- 用
List.copyOf(list)(Java 10+)或Collections.unmodifiableList(...) - 用
Instant替代Date(天然不可变) - 返回新数组:
return Arrays.copyOf(this.internalArray, this.internalArray.length);

















