java怎么创建不可变类
- 后端开发
- 2025-07-12
- 3350
Java中,不可变类(Immutable Class)是指其实例一旦创建后,其状态(即对象的属性值)就无法被修改的类,这种设计模式在多线程环境、函数式编程以及需要确保数据完整性的场景中非常有用,以下是如何在Java中创建一个不可变类的详细步骤和注意事项:
声明类为final
将类声明为final可以防止其他类继承并修改其行为,这是创建不可变类的第一步,因为子类可能会重写方法,从而改变类的行为,破坏不可变性。
public final class ImmutableClass {
    // 类内容
} 
将所有字段声明为private和final
字段应声明为private以隐藏内部实现细节,并声明为final以确保它们只能在构造函数中初始化一次,之后无法更改。
public final class ImmutableClass {
    private final int id;
    private final String name;
    // 其他字段
} 
不提供setter方法
为了保持不可变性,不应提供任何可以修改字段值的setter方法,只能提供getter方法来允许外部代码读取字段值。

public final class ImmutableClass {
    private final int id;
    private final String name;
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    // 无setter方法
} 
通过构造函数初始化所有字段
由于字段是final的,必须在构造函数中进行初始化,可以提供一个或多个带参数的构造函数来设置对象的初始状态。
public final class ImmutableClass {
    private final int id;
    private final String name;
    public ImmutableClass(int id, String name) {
        this.id = id;
        this.name = name;
    }
    // getter方法
} 
深拷贝可变对象
如果类中包含可变对象的引用(如集合、数组等),需要在构造函数或方法中进行深拷贝,以防止外部代码通过引用修改对象状态。
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
public final class ImmutableClass {
    private final int id;
    private final String name;
    private final List<String> items;
    private final Date date;
    public ImmutableClass(int id, String name, List<String> items, Date date) {
        this.id = id;
        this.name = name;
        // 深拷贝集合
        this.items = new ArrayList<>(items);
        // 深拷贝日期(注意Date本身是可变的,应使用不可变类型如LocalDate)
        this.date = new Date(date.getTime());
    }
    // getter方法
} 
使用不可变集合
对于集合类型的字段,可以使用Collections.unmodifiable方法返回不可变视图,或者使用Java 9及以上版本的List.of(), Set.of()等工厂方法创建不可变集合。

import java.util.Collections;
import java.util.List;
public final class ImmutableClass {
    private final int id;
    private final String name;
    private final List<String> items;
    public ImmutableClass(int id, String name, List<String> items) {
        this.id = id;
        this.name = name;
        // 使用不可变集合视图
        this.items = Collections.unmodifiableList(new ArrayList<>(items));
    }
    // getter方法
} 
谨慎处理可变类型
某些Java标准库中的类(如Date、StringBuffer等)是可变的,应避免在不可变类中直接暴露它们的引用,如果必须使用,应进行深拷贝或转换为不可变类型(如LocalDate)。
import java.time.LocalDate;
public final class ImmutableClass {
    private final LocalDate date;
    public ImmutableClass(LocalDate date) {
        this.date = date; // LocalDate本身是不可变的
    }
    // getter方法
} 
提供静态工厂方法(可选)
虽然不是创建不可变类的必要条件,但提供静态工厂方法可以增加灵活性,例如缓存实例、验证参数等。
public final class ImmutableClass {
    private final int id;
    private final String name;
    private ImmutableClass(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public static ImmutableClass of(int id, String name) {
        return new ImmutableClass(id, name);
    }
    // getter方法
} 
重写equals()和hashCode()方法(可选)
根据需求,可以重写equals()和hashCode()方法,以便在集合中使用不可变对象,注意,这些方法应基于不可变字段进行比较。
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    ImmutableClass that = (ImmutableClass) o;
    return id == that.id && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
    return Objects.hash(id, name);
} 
示例:完整的不可变类
以下是一个完整的不可变类示例,结合了上述所有要点:

import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.ArrayList;
public final class Person {
    private final int id;
    private final String name;
    private final LocalDate birthDate;
    private final List<String> hobbies;
    public Person(int id, String name, LocalDate birthDate, List<String> hobbies) {
        this.id = id;
        this.name = Objects.requireNonNull(name);
        this.birthDate = Objects.requireNonNull(birthDate);
        // 深拷贝集合并创建不可变视图
        this.hobbies = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(hobbies)));
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public LocalDate getBirthDate() {
        return birthDate;
    }
    public List<String> getHobbies() {
        return hobbies;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id && Objects.equals(name, person.name) && Objects.equals(birthDate, person.birthDate) && Objects.equals(hobbies, person.hobbies);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name, birthDate, hobbies);
    }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", birthDate=" + birthDate +
                ", hobbies=" + hobbies +
                '}';
    }
} 
相关问答FAQs
Q1: 为什么不可变类在多线程环境中更安全?
A1: 不可变类的对象一旦创建,其状态就无法被修改,这意味着多个线程可以共享同一个不可变对象而无需同步,因为它们无法改变对象的状态,从而避免了并发修改导致的数据不一致问题,不可变对象也天然是线程安全的,因为它们没有可变状态,不会受到其他线程的影响。
Q2: 如果类中包含可变对象的引用,如何确保不可变性?
A2: 如果类中包含可变对象的引用(如集合、数组、Date等),需要在构造函数或方法中进行深拷贝,对于集合,可以使用new ArrayList<>(collection)创建一个新的列表副本,并使用Collections.unmodifiableList()将其包装为不可变视图,对于Date,应使用new Date(date.getTime())创建一个新的Date对象,或者更好的做法是使用java.time包下的不可变类型(如LocalDate、LocalDateTime等),这样可以确保即使外部代码尝试修改原始对象,也不会影响到不可变类的内部状态
 
  
			