String
String类是final类型的,所以使用String定义的字符串是一个常量,因此他一旦创建,其内容和长度是不可改变的。真的是这样简单的吗?
String不可变详解
定义字符串时,在String源码中实际是将字符串赋值给了一个char类型的数组。下面都称为字符数组。
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
/** The value is used for character storage. */
private final char value[];
我自己之前以为String不可变是因为字符数组被final修饰。
其实不然,final修饰引用类型,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,也就是一直引用同一个对象,但这个对象完全可以发生改变。
拿下面的例子来说,char类型的数组被final修饰,表示value不可指向新的数组,但是当前数组里面的值是可变的。
可以看出修改数组中的值是可以的,但是让当前变量指向新数组时会报错。
综上所述,String不能被改变最主要的是字符数组被private修饰。而且String也没有提供可以修改字符数组的api,外界不可访问value[],自然String就不能被修改。
为什么String要设计成不可变
当我们使字面量进行创建字符串时,会在常量池中查找是否有相同值的字符串,如果有则会返回其引用,否则会创建新的字符串,并返回新字符串的引用。
可以看到上述代码str和str1的地址是相同的。

在上述代码中,str的字符串值是“123”,当创建str1时,会从字符串常量池找到一开始创建的str,并返回引用给str1。因此str和str1的引用地址是相同的,如果字符串可变,我们给str重新赋值为“456”,那么str1也会变成456。
看一下打印结果
可以看到str1的值是没有变的,还是123。
如果str1的值会变的话,肯定会影响使用,假如在其他业务逻辑中使用了str1,是不是会造成很严重的后果呢。
所以字符串设计成不可变,可以保证数据的准确性。如果String可变的话,会导致别人在使用时,出现结果不一致的情况。字符串常量池本来就是提供字符串复用的,如果字符串可变,那么复用也就是无稽之谈了。