Java不可变类通过private final字段、无setter、构造器完全初始化、防御性拷贝可变对象及类声明final来保障状态不可变,从而实现高级封装,确保数据完整性、线程安全与可预测性。

Java 中通过不可变类实现数据封装,核心是让类的实例一旦创建,其状态(即字段值)就无法被修改。这天然保障了数据的完整性、线程安全性与可预测性,是封装思想的高级体现。
不可变类的关键设计原则
要真正实现不可变性,需同时满足以下条件:
- 所有字段必须是 private 且 final:阻止外部访问和内部重赋值
- 不提供任何修改字段的 public 方法(如 setter):避免通过行为改变状态
- 构造器必须完成对象的完全初始化:确保对象一创建就处于有效、完整状态
- 若包含可变对象引用(如 ArrayList、Date),需防御性拷贝:防止外部通过引用间接修改内部状态
- 类本身声明为 final(推荐):防止子类通过继承破坏不可变性
典型示例:封装用户信息的不可变类
比如一个 User 类,只允许读取姓名和年龄,不允许后续修改:
public final class User {
private final String name;
private final int age;
public User(String name, int age) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be null or empty");
}
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
this.name = name.trim();
this.age = age;
}
// 只提供 getter,不提供 setter
public String getName() { return name; }
public int getAge() { return age; }
// 可选:重写 equals/hashCode/toString 提升可用性
}
这个类一旦创建,name 和 age 就固定不变,调用方无法篡改,也无需担心多线程竞争。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
处理可变成员时的防御性拷贝
如果封装的数据本身是可变的(如集合、数组、Date),直接返回引用会破坏不可变性。此时需在 getter 中返回副本:
public final class Order {
private final List<String> items;
public Order(List<String> items) {
// 构造时就做防御性拷贝
this.items = new ArrayList<>(Objects.requireNonNull(items));
}
// getter 也返回不可修改视图或新副本
public List<String> getItems() {
return Collections.unmodifiableList(items); // 安全只读视图
// 或:return new ArrayList<>(items); // 完全独立副本
}
}
这样即使调用方拿到 getItems() 结果并尝试修改,也不会影响原始 Order 实例的状态。
不可变类与封装的关系
封装的本质是隐藏内部实现、暴露受控接口。不可变类把“受控”做到极致——它不只隐藏字段,还彻底关闭了所有修改通道。相比普通封装(有 setter 的类),它消除了因意外或恶意调用导致状态不一致的风险,让 API 更可靠、更易推理。尤其在函数式编程风格、并发场景或作为 Map 键/集合元素时,优势尤为明显。

















