Java不可变类案例

wen java案例 2

本文目录导读:

Java不可变类案例

  1. 经典不可变类案例
  2. 使用Builder模式的不可变类
  3. 使用record实现不可变类(Java 14+)
  4. 完整的测试示例
  5. 不可变类的线程安全验证

我来给你展示一个完整的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("所有线程完成,数据一致,无竞态问题");
    }
}
  1. 遵守5条规则:final类、private final字段、无setter、构造器初始化所有字段、防御性拷贝
  2. 可变对象处理:使用防御性拷贝或返回不可修改视图
  3. 使用Builder模式:字段较多时提高可读性
  4. 考虑使用record:Java 14+简化不可变类的创建
  5. 实现equals和hashCode:基于不可变字段
  6. 文档说明:在类注释中说明是不可变类
  7. 性能考虑:过多的小对象创建可能影响性能,考虑缓存

不可变类在函数式编程、并发编程中有广泛的应用,是现代Java编程的重要技巧。

抱歉,评论功能暂时关闭!