前言
Java中的String
是不可变的,这是程序员普遍都知道的,但是很多蛋疼的面试官都喜欢问为什么String
要被设计成不可变的,这样有什么好处等等...本章就简单讲述一下该题。
Final的不可变
String
创建好后真的不可变吗? 相信很多人都听过这个疑问,有人小伙伴说通过反射可以修改String
,如下:
public static void main(String[] args) throws Exception{
String text = "Hello";
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(text);
value[0] = 's';
System.out.println(text);//sello
System.out.println(text == "Hello"); //true
}
大家可以运行这个案例看一下,看看其中有什么问题。这里我们通过反射将text
的值Hello
修改为了sello
,然后我们声明了一个Hello
字符串与之引用比较,它们结果居然相等?
String
底层其实是一个char
数组,并且这个数组是被final
所修饰,那这个不可变
,指的是什么呢?这里需要特意说明一下: 被final
所修饰的变量,不可变的是它的引用
,而不是它的值。表示这个属性的引用,无法重新指向一个对象。
上边这个案例其实就是修改了char
数组中索引为0的元素的值,其实本质上该String
还是不可变的。
String不可变的好处
- 安全性
String
是Java中最基础也是使用最频繁的类型之一,而不可变类型的好处就是安全,它能避免很多关于安全性的问题。 - 缓存
Java中的每一个String
都会缓存到字符串常量池
中,以此达到复用效果, 节省内存空间,如果String
是可变的,我们在某一处修改了String
那么所有引用该常量的地方将会全部跟随修改。这可能就直接违背了字符串常量池
存在的意义。 - 线程安全
String
的所有方法都不会修改原有对象,它都会创建一个新的String
,所有的进一步操作都会在新的String
上,所以在多线程环境下也是可以直接使用的。 - 性能
String
作为Map
容器的最爱,其很大原因就是因为String
是不可变的,我们都知道HashMap
的key是要使用对象的hashCode()
方法来进行取模,而String
的hashcode
只会计算一次,因为他是不可变的,计算一次后就会缓存起来。也就是说String
的hashcode
也是不可变的,这就能很好的满足Map
的key要求。