之前面试被问到,如何不改变字符串内存地址,修改它的内容。没答上来,最后问了面试官如何实现,答曰:反射。
正好看到一篇文章:
https://blog.youkuaiyun.com/a925907195/article/details/46975171
自己也记录一下
String str01 = "aaa";
String str02 = "aaa";
String str03 = "bbbb";
System.out.println(str01.hashCode() + "/" + str02.hashCode() + "/" + str03.hashCode());
Field field = str02.getClass().getDeclaredField("value");
field.setAccessible(true);
field.set(str02, new char[]{'b', 'b', 'b', 'b'});
System.out.println(str01 + "/" + str02);
/* jdk7以后已不包含 private final int count;
field = str02.getClass().getDeclaredField("count");
field.setAccessible(true);
field.set(str02, 4);*/
System.out.println(str01 + "/" + str02);
System.out.println(str01.hashCode() + "/" + str02.hashCode() + "/" + str03.hashCode());
field = str02.getClass().getDeclaredField("hash");
field.setAccessible(true);
field.set(str02, 0);
System.out.println(str01.hashCode() + "/" + str02.hashCode() + "/" + str03.hashCode());
System.out.println(str01 == str02);
System.out.println(str02 == str03);
运行结果
96321/96321/3016832
bbbb/bbbb
bbbb/bbbb
96321/96321/3016832
3016832/3016832/3016832
true
false
- 根据Java String的特性,str01和str02指向同一个内存地址,所以str01,str02始终保持一致。
- String不能修改的本质是一个final的字符数组(private final char value[];),但是final只在编译时有效,而运行期间是没有作用的,所以可以通过反射修改数组的值。
- 代码中修改后字符串的hashcode之所以没有变化,是因为String对象会缓存hashcode,去掉缓存就可以看到变化。
- 根据Java String的特性,上面代码创建的所有字符串对象都保存在String Pool中,代码中str02与str03的内容相同,地址却不同,所以String Pool中就存在两个相同的字符串。
至于 hashcode 为什么没有改我们看源码也很清楚
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
java为了提高效率, 只再第一次调用hashcode函数时生成一次hash, 并把值存在这个实例里面。 以后即使value数组的值变化了,也不再生成新的hash。