为什么说String对象一旦创建,其值是不可修改的
在Java中将String设计成不可变的是综合考虑到各种因素的结果,需要综合考虑内存、同步、数据结构以安全方面的考虑。
String被设计成不可变的主要目的是为了安全和高效(效率)。
1)字符串常量池的需要(共享性)
字符串常量池是Java堆内存中的一个特殊的存储区域,当创建一个String对象时,假如此字符串值已经在常量池中,则不会创建新的对象,而是引用已经存在的对象。这样做能够减少 JVM 的内存开销,提高效率
当使用加号(+)连接多个字符串时,由于String是不可变的,每次连接操作都会创建一个新的String对象,这样可以避免频繁修改字符串带来的性能损耗
如下代码所示,将会在堆内存中只创建一个实际的String对象
String str1 = "abcd"; String str2 = "abcd";
比如引用 s1和 s2 都是指向常量池的同一个对象 “abcd”,如果 String 是可变类,引用 str1 对 String 对象的修改,会直接导致引用 str2 获取一个错误的值。这样的话,那么常量池就没有存在的意义了
2)允许String对象缓存HashCode(缓存hash值)
Java中String对象的哈希吗被频繁的使用,比如在HashMap等容器中。
String类的不变性保证了hash码的唯一性,因此可以放心地进行缓存,它的hashcode值在创建时就被计算好并缓存起来,这样可以提高字符串作为HashMap的键时的性能
3)安全性
字符串被许多的Java类库用来当做参数,例如网络连接地址URL、文件路径、反射机制所需要的String参数等,假如String不是固定不变的,将会引起各种安全隐患,它们的值在创建后不能被修改。这有助于确保数据的一致性和可靠性,防止意外的数据修改
不可变对象天生就是线程安全的。
4) 多线程(线程安全)
多线程中,可变对象的值很可能被其他线程改变,造成不可预期的结果。而不可变的 String 可以自由在多个线程之间共享,不需要同步处理,多个线程可以同时访问同一个String对象而无需担心数据竞争问题,从而保证了线程安全性
String是如何设置为不可变的?
-
字段使用final修饰:String类中的字符数组byte[] value(jdk1.8是char[] value)被声明为final,表示一旦赋值就不能再改变。
-
使用private修饰属性:String类内部的属性都是私有的,外部无法直接修改它们的值。
-
不提供setter方法:String类没有提供用于修改字符串内容的公共setter方法。如果需要修改字符串,将会创建一个新的String对象。
-
方法返回新实例:对String对象进行任何操作(如拼接、替换等),都会返回一个新的String实例,原始的String对象保持不变。
-
静态工厂方法:String类提供了大量静态工厂方法来创建新的String实例,而不是通过公共构造器直接创建实例,这样可以控制String对象的生成过程。
-
字符串连接时使用StringBuilder或StringBuffer:在对字符串进行拼接操作时,String类内部并不是直接对原始字符串对象进行修改,而是利用StringBuilder或StringBuffer来处理字符串拼接,最终生成一个新的String对象。