不可变类是指在创建后其状态(对象的字段)无法被修改的类。一旦对象被创建,它的所属性都不能被更改。这种类型的实例在整个生命周期内保持不变。

特征

  1. 声明类为final,防止子类继承。
  2. 类的所有字段都是privatefinal,确保它们在初始化后不能被更改。
  3. 通过构造函数初始化所有字段。
  4. 不提供任何修改对象状态的方法(如setter方法)。
  5. 如果类包含可变对象的引用,确保这些引用在对象外部无法被修改。例如getter方法中返回对象的副本(new一个新的对象)来保护可变对象。

不可变类的优缺点

优点:

  1. 线程安全:由于不可变对象的状态不能被修改,它们天生是线程安全的,在并发环境中无需同步。
  2. 缓存友好:不可变对象可以安全地被缓存和共享,如String 的字符串常量池。
  3. 防止状态不一致:不可变类可以有效避免因意外修改对象状态而导致的不一致问题。

缺点:

性能问题;不可变对象需要在每次状态变化时创建新的对象,这可能会导致性能开销,尤其是对于大规模对象或频繁修改的场景(例如String频繁拼接)。

如何实现一个不可变类?

String就是典型的不可变类,可以参考。
下面是String的部分源码(jdk-17.0.8):

1
2
3
4
5
6
7
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
@Stable
private final byte[] value;

}

可以看到String本质是一个数组(jdk1.8及以前String使用的是char数组,jdk1.9及以后使用的是byte数组),用final修饰,不过final限制不了数组内部的数据,所以value用private修饰,并且没有暴露set方法。
当有修改需求时,比如replace方法,这时候就会返回一个新对象作为结果。

1
2
3
4
5
6
7
8
9
10
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
: StringUTF16.replace(value, oldChar, newChar);
if (ret != null) {
return ret;
}
}
return this;
}