学习java可变数据类型和不可变数据类型时,一定会了解到String类是不可变的。
但是我们经常使用这样的方式来“改变”String。
String a = "abc";
a + = "123";
//或者
String b = "efg";
b = "456";
为什么这里的String改变了呢?
原因如图所示
执行a + = "123"并不是把"abc"改变成"abc123",而是创建了一个新的对象"abc123"后,再把a的引用指向"abc123",这时候"abc"不再被引用了,后续会被回收。而b也是同理。
我们来看String类的源码。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];}
其中,String类的值保存在value数组中,而这个数组被private final修饰,意味着一旦被初始化后,就不能修改其中的值。例如a = "abc"后,这个String类中value数组就保存了"abc"且不会发生变化。然而,在a + = "123"后,并不是对原有的类操作,而是创建了一个新的String类返回给a。这意味着"abc"所在的地址空间中依然保存着"abc",而在另一个空间里创建了"abc123",然后将a指向它。
但是,问题又来了。我们看到String源码中,String类是被final修饰的。很多人会疑惑,包括我自己看到这里时也会疑惑,不是说fianl修饰的引用类型一旦指向确定的对象,便不会再改变了吗?
例如我们用final修饰一个数组
private final int[] a = {1};
a[0] = 2;
这里数组指向的内存空间是固定的,所谓用final修饰后的不可变性,但是数组里面的值还可以改变,但是如果a = new int[1]就会报错。
再来看String,为什么String用final修饰,但是如上面说的指向改变了呢?
原因是,final修饰变量的时候,基础类型值不可变,引用类型引用地址不可变。但是这里的final修饰的是一个类,final修饰类的时候表示这个类不能被继承。
当我们String a的时候,并没有对对象a修饰final,所以这个时候,它的引用是可以指向新的String类的。但是当我们用final修饰a时,这时候再改变就会报错。如图所示。
希望能解答你的疑惑。