本文目录导读:

我来给你展示一个完整的Java不可变类案例,包含多个实例和最佳实践。
经典不可变类案例
基本不可变类
import java.util.Date;
import java.util.Objects;
/**
* 一个经典的不可变类示例
* 实现不可变类的5条规则:
* 1. 类用final修饰
* 2. 所有字段用private final修饰
* 3. 不提供修改字段的方法(setter)
* 4. 构造器需要初始化所有字段
* 5. 引用类型字段需要深拷贝保护
*/
public final class Person {
// 1. 所有字段都是private final
private final String name;
private final int age;
private final Date birthDate; // 可变对象
// 2. 构造器初始化所有字段
public Person(String name, int age, Date birthDate) {
this.name = name;
this.age = age;
// 防御性拷贝:防止外部引用修改内部状态
this.birthDate = new Date(birthDate.getTime());
}
// 3. 只提供getter方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 4. 返回防御性拷贝,不直接返回引用
public Date getBirthDate() {
return new Date(birthDate.getTime());
}
// 5. equals和hashCode方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(birthDate, person.birthDate);
}
@Override
public int hashCode() {
return Objects.hash(name, age, birthDate);
}
@Override
public String toString() {
return String.format("Person{name='%s', age=%d, birthDate=%s}",
name, age, birthDate);
}
}
使用Builder模式的不可变类
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 使用Builder模式创建不可变类
* 适合字段较多的场景
*/
public final class Employee {
private final String employeeId;
private final String name;
private final String department;
private final double salary;
private final List<String> skills; // 可变集合
private final Address address; // 引用另一个对象
// 私有构造器,必须传入Builder
private Employee(Builder builder) {
this.employeeId = builder.employeeId;
this.name = builder.name;
this.department = builder.department;
this.salary = builder.salary;
// 深拷贝或不可修改集合
this.skills = builder.skills == null ?
Collections.emptyList() :
Collections.unmodifiableList(new ArrayList<>(builder.skills));
// 不可变对象引用可以直接赋值
this.address = builder.address;
}
// Getter方法
public String getEmployeeId() {
return employeeId;
}
public String getName() {
return name;
}
public String getDepartment() {
return department;
}
public double getSalary() {
return salary;
}
// 返回不可修改的集合视图
public List<String> getSkills() {
return skills;
}
// 返回对象引用(Address是不可变类)
public Address getAddress() {
return address;
}
// 静态Builder类
public static class Builder {
// 必填字段
private final String employeeId;
private final String name;
// 可选字段,设置默认值
private String department = "未分配";
private double salary = 0.0;
private List<String> skills;
private Address address;
// 构造器必须包含必填字段
public Builder(String employeeId, String name) {
this.employeeId = employeeId;
this.name = name;
}
// 可选字段的链式调用方法
public Builder department(String department) {
this.department = department;
return this;
}
public Builder salary(double salary) {
this.salary = salary;
return this;
}
public Builder skills(List<String> skills) {
this.skills = new ArrayList<>(skills);
return this;
}
public Builder address(Address address) {
this.address = address;
return this;
}
// build方法创建Employee实例
public Employee build() {
return new Employee(this);
}
}
@Override
public String toString() {
return String.format("Employee{id='%s', name='%s', dept='%s', salary=%.2f, skills=%s}",
employeeId, name, department, salary, skills);
}
}
/**
* 另一个不可变类,作为Employee的属性
*/
public final class Address {
private final String street;
private final String city;
private final String zipCode;
public Address(String street, String city, String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
// 所有字段都有getter,没有setter
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
}
使用record实现不可变类(Java 14+)
/**
* Java 14+ 使用record定义不可变类
* record自动生成:构造器、equals、hashCode、toString
*/
public record Product(String id, String name, double price) {
// 紧凑构造器:可以添加校验逻辑
public Product {
Objects.requireNonNull(id, "id不能为空");
Objects.requireNonNull(name, "name不能为空");
if (price < 0) {
throw new IllegalArgumentException("价格不能为负数");
}
}
// 可以添加方法
public boolean isExpensive() {
return price > 10000;
}
// 静态方法
public static Product of(String id, String name, double price) {
return new Product(id, name, price);
}
}
完整的测试示例
public class ImmutableDemo {
public static void main(String[] args) throws Exception {
// 1. 测试基本不可变类
System.out.println("=== 测试基本不可变类 ===");
Date birthDate = new Date();
Person person = new Person("张三", 25, birthDate);
// 尝试修改原始Date对象
birthDate.setTime(0L); // 修改传入的Date
System.out.println("Person的birthDate: " + person.getBirthDate());
// 尝试修改getter返回的对象
Date bd = person.getBirthDate();
bd.setTime(123456789L); // 修改返回的Date
System.out.println("再次访问birthDate: " + person.getBirthDate());
// 2. 测试Builder模式
System.out.println("\n=== 测试Builder模式 ===");
List<String> skills = new ArrayList<>();
skills.add("Java");
skills.add("Spring");
Employee employee = new Employee.Builder("EMP001", "李四")
.department("技术部")
.salary(15000)
.skills(skills)
.address(new Address("北京路1号", "北京市", "100000"))
.build();
// 修改外部skills列表
skills.add("Python");
System.out.println("Employee技能: " + employee.getSkills());
// 3. 测试record
System.out.println("\n=== 测试record ===");
Product product = new Product("P001", "笔记本电脑", 9999.99);
System.out.println("Product: " + product);
System.out.println("是否昂贵: " + product.isExpensive());
// 4. 验证不可变性
System.out.println("\n=== 验证不可变性 ===");
testImmutability(person, employee, product);
}
private static void testImmutability(Person person, Employee employee, Product product) {
// 检查类是否是final
System.out.println("Person类是否final: " + Character.isUpperCase(
Person.class.getModifiers() & 16 // 16 = final modifier
));
// 尝试各种修改方式(编译错误注释掉)
// person.name = "王五"; // 编译错误:无法赋值
// person.age = 30; // 编译错误:无法赋值
// employee.getSkills().add("Go"); // 运行错误:UnsupportedOperationException
// product.setName("新名字"); // 编译错误:没有setter
}
}
不可变类的线程安全验证
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 不可变类天然线程安全
*/
public class ImmutableThreadSafetyDemo {
public static void main(String[] args) throws InterruptedException {
final int threadCount = 100;
final CountDownLatch ready = new CountDownLatch(threadCount);
final CountDownLatch start = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(threadCount);
ExecutorService pool = Executors.newFixedThreadPool(threadCount);
// 多个线程共享同一个不可变对象
Person sharedPerson = new Person("共享", 30, new Date());
for (int i = 0; i < threadCount; i++) {
final int id = i;
pool.submit(() -> {
try {
ready.countDown();
start.await();
// 并发读取不可变对象
for (int j = 0; j < 1000; j++) {
System.out.println("线程" + id + ": " + sharedPerson.getName()
+ ", " + sharedPerson.getAge());
// 不可变对象无需同步,不会出现竞态条件
}
done.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
ready.await();
System.out.println("所有线程就绪,开始并发访问...");
start.countDown();
done.await();
pool.shutdown();
System.out.println("所有线程完成,数据一致,无竞态问题");
}
}
- 遵守5条规则:final类、private final字段、无setter、构造器初始化所有字段、防御性拷贝
- 可变对象处理:使用防御性拷贝或返回不可修改视图
- 使用Builder模式:字段较多时提高可读性
- 考虑使用record:Java 14+简化不可变类的创建
- 实现equals和hashCode:基于不可变字段
- 文档说明:在类注释中说明是不可变类
- 性能考虑:过多的小对象创建可能影响性能,考虑缓存
不可变类在函数式编程、并发编程中有广泛的应用,是现代Java编程的重要技巧。