关于这个问题,网上有人说,是因为String类被写成final或者String中的成员变量value数组被写成final,但其实并不是,下面做一个实验
public final class MyString {
public final char[] value = {'z'};
}
首先我们定义了一个类Mystring,并且类和成员变量都被设置成final
public class Main {
public static void main(String[] args) {
MyString myString = new MyString();
System.out.println(myString.value);
myString.value[0] = 'a';
System.out.println(myString.value);
myString.value[0] = 'b';
System.out.println(myString.value);
}
}
执行结果
z
a
b
这说明仅仅把类和成员变量设置成final无法实现不可变,为了实现不可变我们还需要把这个成员变量设置成private不可见。
继续我们的实验
public final class MyString {
private final char[] value = {'z'};
}
这时再去执行上面的main函数,有如下结果,
E:\MIT6.830\Test_7\src\Main.java:12:36
java: value 在 MyString 中是 private 访问控制
这时候我们无法直接修改value的值,但是这就可以了吗?当然还没有
为了保证string的不可变我们还要继续做到以下几点
- 首先设置内部成员变量的访问修饰符为private,这样就无法在类的外部直接访问到这个成员变量
- 其次我们必须保证String类不提供成员方法去修改value,String的成员方法不是直接修改value而是通过新建一个String,并且把旧的string中的值复制到新的string对象中去。
- 在value上加final保证这个数组的引用不会被修改而指向另一个数组
- 在String上加final保证String没有子类,因为子类可能提供方法去修改value,子类可以赋值给父类引用进而破坏String的不可变特性
保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。