前言

最近可被这个intern方法和串池整的团团转,网上查各种资料,要么不全甚至有错误,要么就是jdk1.6、7、8反复横跳,弄得我晕头转向的。下面针对两个争议很大的问题经过整理和测试,结论如下:

一、字符串常量池(串池)中存的是对象和引用

                                                                                  图1  jvm-java8 内存结构图

                                             

                                                                图2  堆和串池(串池也在堆中)中对象的存储位置

结论:对象实例存储在堆的实例区,字符串字面量对应的对象存储在堆的字符串常量池。串池同时存引用和对象,其中引用存在StringTable中,如图2。

二、intern方法注释中的 added to the pool 到底是什么含义

结论:this String Object is added to the pool,jdk1.8+是直接将堆中String对象移除,并添加到串池中。jdk1.6是拷贝了一份堆中的对象到串池,堆中原有对象并未移除。

因此,intern()方法的定义是:

在jdk11+中,调用intern()方法的对象s,如果在串池中找不到对象t,使得s.equals(t) == true(Object方法),则直接将堆中String对象移除,添加到串池中,并返回该对象(串池对象)的引用。如果找得到对象t,使得s.equals(t) == true,则直接返回串池对象的引用

注释已明确说明this String object,即字符串对象,而不是the reference of String object。因此jdk11之后串池中是有对象的

三、测试

public class Test {

    public static void main(String[] args) {
        String s1 = new String("Ja") + new String("va"); //new StringBuilder().append("Ja").append("va").toString()
        //此时对象存储情况:
        //串池:["Ja","va"] 2个
        //堆:new String("Ja"),new String("va"),new String("Java") 3个

        String s = s1; //引用s和s1均指向堆中的对象:new String("Java")
        String s2 = s1.intern(); //串池中没有"Java"对象,将堆中的对象new String("Java")添加到串池中
        //此时对象存储情况:
        //串池:["Ja","va","Java"] 3个
        //堆:new String("Ja"),new String("va") 2个

        System.out.println(s1 == s2); //true
        System.out.println(s == s1); //true
        System.out.println(s == s2); //true
        //三个引用均指向串池中的"Java"对象
    }

}

                                                                                               jdk11+

public class Test {

    public static void main(String[] args) {
        String s1 = new String("Ja") + new String("va"); //new StringBuilder().append("Ja").append("va").toString()
        //此时对象情况:
        //串池:["Ja","va"] 2个
        //堆:new String("Ja"),new String("va"),new String("Java") 3个

        String s = s1; //引用s和s1均指向堆中的对象new String("Java")
        String s2 = s1.intern(); //串池中没有"Java"对象,将堆中的对象 拷贝 到串池中
        //此时对象情况:
        //串池["Ja","va","Java"] 3个
        //堆 new String("Ja"),new String("va"),new String("Java") 3个

        System.out.println(s1 == s2); //false
        System.out.println(s == s1); //true
        System.out.println(s == s2); //false

    }

}

                                                                                             jdk1.6、1.8

如图:                                                  (jdk1.6的串池在方法区中。这里为了更直观,因此没有做修改)

 

参考

intern的c++源码:https://blog.csdn.net/kq1983/article/details/116863941

关于字符串池存储的是引用还是对象的争议_@baseException的博客-CSDN博客_字符串常量池存储的是对象还是引用

https://github.com/Seazean/JavaNote

https://javaguide.cn/home.html#%E9%A1%B9%E7%9B%AE%E7%9B%B8%E5%85%B3

内容经笔者整理。如有勘误,非刻意误导!欢迎在评论区留言

感谢@ZCVF对内容的指正


版权声明:本文为m0_55913429原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_55913429/article/details/127867743