跳转至

Java 不可变类:概念、使用与最佳实践

简介

在 Java 编程中,不可变类(Immutable Class)是一种非常重要的设计模式。不可变类的实例一旦创建,其状态就不能被修改。这种特性带来了诸多好处,如线程安全、易于理解和维护等。本文将详细介绍 Java 不可变类的基础概念、使用方法、常见实践以及最佳实践,帮助读者深入理解并高效使用 Java 不可变类。

目录

  1. 基础概念
  2. 使用方法
  3. 常见实践
  4. 最佳实践
  5. 小结
  6. 参考资料

基础概念

定义

不可变类是指一旦创建,其对象的状态就不能被修改的类。也就是说,在对象创建之后,任何试图修改其状态的操作都不会改变对象本身,而是会返回一个新的对象。

优点

  • 线程安全:由于不可变对象的状态不能被修改,多个线程可以同时访问同一个不可变对象,而无需担心线程安全问题。
  • 易于理解和维护:不可变对象的状态是固定的,这使得代码的行为更加可预测,易于理解和维护。
  • 缓存和复用:不可变对象可以被缓存和复用,因为它们的状态不会改变,不会影响其他部分的代码。

示例

// 一个简单的不可变类
public final class ImmutablePoint {
    private final int x;
    private final int y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

在这个示例中,ImmutablePoint 类是一个不可变类。它的属性 xy 被声明为 final,并且没有提供任何修改这些属性的方法。一旦对象被创建,其状态就不能被改变。

使用方法

创建不可变类的步骤

  1. 声明类为 final:确保类不能被继承,防止子类修改其行为。
  2. 声明所有属性为 final:确保属性一旦被赋值就不能被修改。
  3. 不提供修改属性的方法:如 setter 方法。
  4. 如果属性是引用类型,确保其内部状态也不可变:或者在必要时进行防御性复制。

示例

import java.util.Collections;
import java.util.List;

// 一个包含引用类型属性的不可变类
public final class ImmutablePerson {
    private final String name;
    private final List<String> hobbies;

    public ImmutablePerson(String name, List<String> hobbies) {
        this.name = name;
        this.hobbies = Collections.unmodifiableList(hobbies);
    }

    public String getName() {
        return name;
    }

    public List<String> getHobbies() {
        return hobbies;
    }
}

在这个示例中,ImmutablePerson 类包含一个引用类型的属性 hobbies。为了确保 hobbies 的状态不可变,我们使用 Collections.unmodifiableList 方法创建了一个不可修改的列表。

常见实践

不可变集合

Java 提供了一些不可变集合类,如 Collections.unmodifiableListCollections.unmodifiableSetCollections.unmodifiableMap。这些方法可以将可变集合转换为不可变集合。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ImmutableCollectionExample {
    public static void main(String[] args) {
        List<String> mutableList = new ArrayList<>();
        mutableList.add("apple");
        mutableList.add("banana");

        List<String> immutableList = Collections.unmodifiableList(mutableList);

        try {
            immutableList.add("cherry"); // 会抛出 UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify immutable list.");
        }
    }
}

使用 record 类(Java 14+)

Java 14 引入了 record 类,它是一种特殊的不可变类,用于简单的数据载体。

// 使用 record 类创建不可变对象
record Point(int x, int y) {}

public class RecordExample {
    public static void main(String[] args) {
        Point p = new Point(1, 2);
        System.out.println(p.x()); // 访问属性
    }
}

最佳实践

防御性复制

在构造函数中对引用类型的参数进行防御性复制,防止外部对象修改内部状态。

import java.util.ArrayList;
import java.util.List;

public final class DefensiveCopyExample {
    private final List<String> data;

    public DefensiveCopyExample(List<String> data) {
        this.data = new ArrayList<>(data); // 防御性复制
    }

    public List<String> getData() {
        return new ArrayList<>(data); // 返回副本
    }
}

使用静态工厂方法

使用静态工厂方法创建不可变对象,提供更灵活的创建方式。

public final class StaticFactoryExample {
    private final int value;

    private StaticFactoryExample(int value) {
        this.value = value;
    }

    public static StaticFactoryExample of(int value) {
        return new StaticFactoryExample(value);
    }

    public int getValue() {
        return value;
    }
}

小结

不可变类在 Java 编程中具有重要的地位,它带来了线程安全、易于理解和维护等诸多好处。通过遵循创建不可变类的步骤,合理使用不可变集合和 record 类,以及采用防御性复制和静态工厂方法等最佳实践,我们可以高效地使用不可变类,提高代码的质量和可维护性。

参考资料

  • 《Effective Java》,Joshua Bloch