今天在研究Java中String的不可变性的时候,发现一个有趣的现象,直接上代码:
String str = "Hello String";
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char [])field.get(str);
value[9] = 'o';
System.out.println(str); // Hello Strong
System.out.println(str == "Hello String"); //true
System.out.println(str == "Hello Strong"); //false
上面这段代码逻辑很简单,就是将str原本指向的内容“Hello String”中,字符数组的第10位将字符“i”换成了字符“o”,这时str的输出的内容其实是Hello Strong
,但是我直接把str与常量Hello String
进行==
比较的时候发现返回的竟然是true。
我了解到的是Java中String存在一个常量池的概念,很明显Hello String
作为字面量直接赋值其实就是在常量池中缓存了。
问题:我这里很明显将字符串内部的字符数组中的某一位换掉了,但是在常量池判断是,它仍是将修改的那段内容判断为Hello String
常量了,即使str这时内容已经变成了Hello Strong
,那么在常量池中是如何实现存储的,我的猜测是:是不是在字符串装入常量池的时候,有一个类似于HashMap的那种结构,key就是放入的常量,value就是指向的具体字符数组区域,从而出现了现在这种,即使我修改了字符数组,它的key仍然是之前的字符串常量这种效果,想不通,求哪位大佬解答,不胜感激~~
写在正文之前的话,本来下面的回答2天前就写好准备提交。。。结果一提交。。。emmm。。。害。。。正碰上。。。o(╯□╰)o
题主,你好,我来分享哈我的看法,做个补充,希望对你有所帮助,
其实正如_TNT_所说,字符串常量池确实就是一个
hash
表,也确实key
为字符串做hash
之后得到的,而value
就是String
对象的地址所以只要你把字符串常量池理解为
String
对象的一个引用集合(collection)就可以了(注:这里只是方便理解所以说是引用的集合,但实际上引用只会出现在栈上,而字符串常量池现在在堆里,里面只有地址,没有引用,所以下面第二张图画的是地址)
注意,这里是对象引用,不是字符串本身,或许很多人听着字符串常量池就会认为里面全是字符串,类似下面这个样子
但是其实是忘了
String
虽然有点特别,但是本质还是一个引用类型嘛,跟我们平常写的类都差不多,只是官方这里做了一个缓存罢了,所以其实字符串常量池是一个String
对象的一个引用集合,类似下面这样牢记字符串常量池里存的只是
String
的对象引用就行了所以现在回过头看看题主的例子:
第一行
String str = "Hello String";
其实就是表明,变量str
本身就是String
对象("Hello String"
)的引用,并且这个引用已经放到了字符串常量池里,且和字符串"Hello String"
做了hash
绑定先不用看接下来你怎么把
"Hello String"
变为"Hello Strong"
,因为你后面的判断是基于==
的,这个是比较引用是否相等的,跟值没啥关系因此
就很好理解了,右边直接使用了字面量
"Hello String"
,那就会去字符串常量池里找,诶,找到了一个,当然这个也和str
相等而
也很好理解了嘛,右边直接使用了字面量
"Hello Strong"
,同样也会字符串常量池里找,找不到,那就新生成一个String
对象,并且把其地址和"Hello Strong"
的hash
绑定起来放到字符串常量池里,那两个地址肯定不一样,所以返回false
如果觉得我说明的还不太清楚,我再举个例子,比如下面这个代码
如果我画图的话,会这样表述
最后我们说说微凉提到的
String
类的intern
方法这个方法其实就是根据当前
String
对象的字符串值去字符串常量池里找,看能不能找到已经存在的String
对象引用,有的话,就返回,没有就把该String
对象的引用hash
进字符串常量池里绑定起来那微凉觉得懵逼的3个
true
其实原理都差不多了,比如:左边嘛
"Hello Strong".intern()
那这肯定返回的是字符串常量池里根据"Hello Strong"
字符串算出的hash
值其对应引用右边呢?
"Hello String".intern()
不难道是根据"Hello String"
字符串算出的hash
值其对应引用么?显然不是谁调用
intern()
方法?"Hello String"
找到的引用"Hello String"
找到的引用肯定是从字符串常量池里找到的引用,这个引用其实跟上面的变量str
是一样的,对吧那
"Hello String".intern()
其实就可以写成str.intern()
,结合之前对intern
方法的描述,str.intern()
也就是表示,用String
对象str
的字符串值去字符串常量池里找,找得到就返回诶~ 通了吧,这里
str
的字符串值可不再是"Hello String"
了啊,而是被古灵精怪的题主改为"Hello Strong"
了啊o(╯□╰)o,用"Hello Strong"
在字符串常量池里找到的引用肯定跟左边的一样啊以上仅是个人观点,仅供参考哈~<( ̄ˇ ̄)/