Java中String的不可变
java.util.String
- 实现方式
String类使用private final两个关键字修饰的char数组来存储字符串中的元素。
final属性保证value地址不能被修改,但这还不够,final只能保证数组引用不会被重新赋值指向新的数组,不能保证value数组元素的值被修改。
private属性要更加关键,它确保value数组不会被外界直接访问,同时,在String的方法中设计师很小心的没有暴露value数组的访问,加上private的限制,使得外界无法修改数组元素。
/** The value is used for character storage. */
private final char value[];
- 缓存hashcode
字符串的hashcode在java中经常被使用,如HashMap。不可变保证了hashcode会一直相同,所以它可以用字段hash缓存起来而不需要担心改变。这也意味着,每次使用String的时候,不需要重新计算hashcode,这使得性能更加有效,所以我们经常会看到map的key使用字符串。
/** Cache the hash code for the string */
private int hash; // Default to 0
- 安全性
字符串经常被用来作为参数来传递,例如文件操作,网络连接等。如果字符串可变,就可能会导致严重的的安全问题。 - 线程安全
String类型的不可变,使得在并发场景中不需要考虑并发访问可能导致的同步问题,也提高了访问性能 - 突破字符串的不可变
上面说了,通过private和final限制加上设计师的小心设计使得String不可变,但这并不是绝对的。使用反射机制我们就能突破j这种限制,实现对字符串的修改。
String string = "asdfg";
System.out.println(string);
java.lang.reflect.Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(string);
System.out.println("value[2]:" + value[2]);
for (int i = 0; i < value.length; i++) {
System.out.print(value[i]);
}
System.out.println();
value[2] = '1';
System.out.println("value[2]:" + value[2]);
System.out.println(string);
输出:
asdfg
value[2]:d
asdfg
value[2]:1
as1fg