String不可变的含义、原因、好处
String不可变的含义
String不可变的含义是:将一个已有字符串”123”重新赋值成”456”,不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。
也就是说:不可变的含义是内部数据不可变,而不是说引用不可变。
String为什么不可变
String的内部数据是一个char数组,是对字符串数组的封装,并且是被final修饰的,创建后不可改变。
1 | package java.lang; |
String不可变的好处
对于String来说,好处是:
- 便于实现字符串池(String pool)
在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。
如果字符串是可变的,某一个字符串变量改变了其值,那么其指向的变量的值也会改变,String pool将不能够实现!
例如下边这个代码,就只会在字符串池创建一个字符串“Hello World!”:
1 | String a = "Hello World!"; |
- 使多线程安全
看下面这个场景,一个函数appendStr()在不可变的String参数后面加上一段“bbb”后返回。appendSb()负责在可变的StringBuilder后面加”bbb”。
1 | public class test { |
如果程序员不小心像上面例子里,直接在传进来的参数上加上“bbb”。因为Java对象参数传的是引用,所有可变的StringBuffer参数就被改变了。可以看到变量sb在Test.appendSb(sb)操作之,就变成了”aaabbb”。
有的时候这可能不是程序员的本意。所以String不可变的安全性就体现在这里。
在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。
- 避免安全问题
在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。
因为String是不可变的,所以它的值是不可改变的。但由于String不可变,也就没有任何方式能修改字符串的值,每一次修改都将产生新的字符串,如果使用char[]来保存密码,仍然能够将其中所有的元素设置为空和清零,也不会被放入字符串缓存池中,用字符串数组来保存密码会更好。
- 加快字符串处理速度
由于String是不可变的,保证了hashCode的唯一性,于是在创建对象时其hashCode就可以放心的缓存了,不需要重新计算。这也就是一般将String作为Map的Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。
在String类的定义中有如下代码:
1 | private int hash; // 用来缓存HashCode |
String的字符数据可变
String类使用char value[]来存字符数据,它的类型为:private final char value[];
看上去它是不可更改的,因为是final类型。注意:final只是表示不能指向其他地址,它里边的内容是可以更改的。
结论:String是可以更改的,使用反射,value.setAccessible(true),然后修改它即可。
1 | import java.lang.reflect.Field; |