参考:
《深入理解java虚拟机》 p56-57
深入分析String.intern和String常量的实现原理
1. new String("abc")创建了几个对象??
记住。这个与JDK版本无关。
如果常量池原来有个“abc”,那么只创建一个对象;
如果常量池中原来没有字符串"abc",那么就会创建两个对象。
2. String.intern()方法(Native方法) 1.6复制的是实例 1.7复制的是引用
jdk1.6时:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。(常量池分配在永久代)
JDK1.7时:Java 7将常量池从PermGen区移到了Java堆区,执行intern操作时,如果常量池已经存在该字符串,则直接返回字符串引用,否则 复制该字符串对象的引用 到常量池中并返回 (首次出现原则)。
具体区别分析见《深入分析String.intern和String常量的实现原理》
3.图解说明
![](https://img.haomeiwen.com/i6085414/a4bf2edcafc3940c.png)
上述代码在JDK1.6时输出false ,false; JDK 1.7时输出true,false
![](https://img.haomeiwen.com/i6085414/2ba5740129573cdd.png)
从这幅图可以看出,在调用s1.intern()时,常量池中不存在StringTest,因此复制了一份StringTest的实例置于常量池中,并返回常量池中StringTest的引用,而s1指向的是Java堆中的StringTest,二者并不相同,返回false;
java字符串本身就在常量池中存在(可能是jvm虚拟机放进去的)。这时候调用s2.intern(),返回的是常量池中java 的引用,s2指向的是Java堆中的java,二者并不相同,返回false。
![](https://img.haomeiwen.com/i6085414/32d4aadb41a2e313.png)
JDK1.7时,常量池被移到了java堆区。在常量池中没有找到StringTest,只是在常量池中记录下首次出现的实例地址,因此s1.intern()和s1指向的是同一个StringTest,所以结果为true;
而在常量池中找到了java,返回的是常量池中java的地址,与s2不想同。
因此JDK1.7中输出如下:
![](https://img.haomeiwen.com/i6085414/9b27d83da8d83c2c.png)
需要注意的是,s3.intern()==s3也返回false。
以下是自己的推测;
(1)根据第1条,new String(“abc”)应该是等价于new StringBuilder(“abc”).toString()。 s3会在字符串常量池中创建“StringTest”对象,也会在对中创建对象。因此s3.intern()中StringTest不符合首次出现,不会复制指向堆中StringTest的引用到常量池中,因此s3.intern()与s3不相等。
(2) 而s1中,应该是常量池存在String,Test。但是不存在StringTest,仍然符合首次出现原则,因此会复制堆中StringTest的引用到常量池中。
上述(2),用“计算机软件进行验证”。
![](https://img.haomeiwen.com/i6085414/185d492053c008de.png)
![](https://img.haomeiwen.com/i6085414/2853e263f4b7c885.png)
从上述结果验证了自己推测的(2)。s1在常量池中建立了“计算机”和“软件”对象。 这样s4.intern()和s5.intern()都不符合首次出现原则,因此返回false。
网友评论