不可变类是指创建后无法修改对象状态的类。String
类是Java中典型的不可变类。
1. 不可变类的特点
- 一旦创建,对象的状态就不能被改变。
- 所有成员变量都是
final
类型。 - 所有可变成员变量都是私有的,并且没有提供修改它们的公共方法。
2. 不可变类的示例
以 String
类为例,执行 s += "a";
实际上是创建了一个新的 String
对象,而原始对象保持不变。
因为无法被修改,所以像执行s += "a";这样的方法,其实返回的是一个新建的String对象,老的s指向的对象不会发生变化,只是s的引用指向了新的对象而已。
3. 不可变类的优点
- 安全性:由于对象状态不可变,可以在多线程环境中安全使用而无需同步。
- 简单性:简化了编程模型,因为不需要考虑对象状态的变化。
4. 性能考虑
- 在字符串拼接频繁的场景下,应避免使用
+
操作符,因为这会导致频繁创建新对象。 - 可以使用
StringBuilder
或StringBuffer
来优化性能。
5. 如何实现一个不可变类?
- 使用
final
修饰类:防止继承。 - 使用
final
修饰所有成员变量:确保引用不可变。 - 使成员变量私有:隐藏类的内部状态。
- 不提供修改成员变量的公共方法:防止外部修改。
示例代码
public final class ImmutableClass {
// String 本质是一个char 数组,然后用 final修饰,不过 final限制不了数组内部的数据,所以这还不够。
//所以value 是用private修饰的,并且没有暴露出set方法,这样外部其实就接触不到value所以无法修改。
private final int value;
public ImmutableClass(int value) {
this.value = value;
}
// 不提供任何修改value的方法
//当然还是有修改的需求,比如replace方法,所以这时候就需要返回一个新对象来作为结果。
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* 避免使用 getfield 指令 */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) { // 如果找到了 oldChar
char[] buf = new char[len];
for (int x = 0; x < i; x++) {
buf[x] = val[x];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, share: true); // share 参数在实际代码中不存在,这里假设为简化说明
} else {
return this; // 如果没有找到 oldChar,返回原字符串
}
} else {
return this; // 如果 oldChar 和 newChar 相同,直接返回原字符串
}
}
}
总结一下就是私有化变量,然后不要暴露set方法,即使有修改的需求也是返回一个新对象。