zoukankan      html  css  js  c++  java
  • String中intern方法的作用

    前言

    读完这篇文章你可以了解,String对象在虚拟机内存中的存放,intern的作用,这么多String对象的创建到底有什么区别,String 创建的对象有几个!!

    正题

    先科普几个知识点
    1.常量池存放于方法区中

    2.jdk1.6 方法区放在永久代(java堆的一部分),jdk1.7 特别将字符串常量池移动到了的堆内存中(使用参数-XX:PermSize 和-XX:MaxPermSize指定大小),jdk1.8放在单独的元空间里面(-XX:MaxMetaspaceSzie设定大小),和堆相独立。所以导致string的intern方法因为以上变化在不同版本会有不同表现。

    3.jdk1.6将Hotspot虚拟机使用永久代来实现方法区,因为方法区的内存回收跟堆内存回收其实没什么区别,这样实现可以用垃圾收集器来管理这部分内存,但这样容易导致内存溢出(达到-XX:MaxPermSize)。

    JDK1.6,JDK1.7常量池的存放如下都存放于堆内存中

    JDK1.8常量池的存放如下


    具体可参考:https://blog.csdn.net/zhyhang/article/details/17246223/

    知道了常量池在内存中的存放后,我们需要先了解一下 String str=“abc”;和 String str =new String(“abc”);的区别

    1.String str=“abc”;

    JDK1.6
    (1) 当常量池中不存在"abc"这个字符串的引用,在堆内存中new一个String对象,复制这个对象加入常量池,返回常量池中的对象。


    (2) 当常量池中存在"abc"这个字符串对象,str指向这个对象的引用;

     

    JDK1.7以上
    (1) 当常量池中不存在"abc"这个字符串的引用,在堆内存中new一个新的String对象,将这个对象的引用加入常量池。(跟1.6的区别是常量池不再存放对象,只存放引用。)
    (2) 当常量池中存在"abc"这个字符串的引用,str指向这个引用;

    2.String str =new String(“abc”);

    单纯的在堆内存中new一个String对象,通过StringBuilder 跟StringBuffer 构建的对象也是一样

    3.intern方法 (1.7版本,返回常量池中该字符串的引用)

    (1) 当常量池中不存在"abc"这个字符串的引用,将这个对象的引用加入常量池,返回这个对象的引用。
    (2) 当常量池中存在"abc"这个字符串的引用,返回这个对象的引用;

    测试代码如下

    String str1 = "计算机";
    String str2 = "计算机";
    System.out.println("str1==str2:" + (str1 == str2));
    
    String str3 = new String("计算机");
    System.out.println("str1==str3:" + (str1 == str3));
    System.out.println("str1==str3.intern():" + (str1 == str3.intern()));
    System.out.println("str2==str3.intern():" + (str2 == str3.intern()));
    
    String str4 = new String("计算机");
    System.out.println("str3==str4:" + (str3 == str4));
    System.out.println("str3.intern()==str4.intern():" + (str3.intern() == str4.intern()));
    
    
    String str5 = new StringBuilder("软件").append("工程").toString();
    System.out.println("str5.intern() == str5:" + (str5.intern() == str5));
    
    String str6 = new String(new StringBuilder("物联网").append("工程").toString());
    System.out.println("str6.intern() == str6:" + (str6.intern() == str6));
    
    String str7 = new String("物联网");
    System.out.println("str7.intern() == str7:" + (str7.intern() == str7));

    JDK1.8输出结果如下:

    str1==str2:true
    str1==str3:false
    str1==str3.intern():true
    str2==str3.intern():true
    str3==str4:false
    str3.intern()==str4.intern():true
    str5.intern() == str5:true
    str6.intern() == str6:true
    str7.intern() == str7:false


    这里着重讲解一下为什么str5,str6和str7的结果不一样,

    str7直接用new String(“物联网”)创建,"物联网"这字符串在一出现就自动创建成对象存放到常量池中,所以常量池里面存放的是"物联网"字符串的引用,并不是str7创建的对象的引用。

    str5是通过StringBuilder构建的 在new StringBuilder(“软件”).append(“工程”).toString方法运行后,"软件工程"这个字符串对象才第一次出现。执行了intern方法后str5才被放到常量池中,此时str5跟str5.intern是同一个对象。

    str6是作为对照组出现,这里为了确认StringBuilder 在toString方法执行后会不会把最终字符串放进常量池。很显然并没有,所以str6的intern才会跟str6是同一个对象。同时它也能验证出str7的new String()方式在初始化的时候就会把"物联网"字符串放进常量池中,同理我们可以得出在str5构建的时候常量池里面加入了"软件","工程"这两个字符串(可以自己动手去验证一下!!)。

    JDK1.7结果如下

    str1==str2:true
    str1==str3:false
    str1==str3.intern():true
    str2==str3.intern():true
    str3==str4:false
    str3.intern()==str4.intern():true
    str5.intern() == str5:true
    str6.intern() == str6:true
    str7.intern() == str7:false
    


    当然JDK1.6我们也能大致明确,输出结果会如下

    str1==str2:true
    str1==str3:false
    str1==str3.intern():true
    str2==str3.intern():true
    str3==str4:false
    str3.intern()==str4.intern():true
    str5.intern() == str5:false
    str6.intern() == str6:false
    str7.intern() == str7:false
    


    分析一下jdk1.6中 str5.intern() == str5的结果为什么为false,这里可以这么看在软件工程这个对象中构建完成之后,常量池里面其实存放的只有“软件”,"工程"这两个字符串,而"软件工程"这个字符串是存放于堆内存中的。所以JDK1.6执行了intern后会把“软件工程”复制到常量池中,并返回常量池中的对象。常量池中的"软件工程"跟堆内存中的软件工程并不是同一个。

    因为没有尝试过,如果jdk1.6有不一致的地方欢迎大家指点。

    总结

    1.String对象可能会在堆内存中分配一块空间创建一个String对象,在常量池中创建该对象的复制jdk 1.6(或引用jdk 1.7及以上),也可能直接指向常量池(永久带)中的引用(例String str=“xxx”)。还有一种可能是直接在堆内存中分配一块空间创建一个String对象(例 String str=new String(xxx))。

    2.intern方法可以看成返回常量池中该字符串对象的引用。如果没有该字符串对象就把这个对象(或引用)加到常量池。

    3.jdk1.6跟jdk1.7以上的区别是当常量池中不存在这个字符串,jdk1.6是直接复制对象到常量池,而jdk1.7以上是把对象的引用加入常量池。

    4.类似于”abc”这样的字符串,在第一次被使用到(比如String a=”abc”或者String a=new String(“abc”)或者"abc".equals(xxx))后就会被加载到常量池中去。

    5.面试问题:

    (1)现在当有人问 String str = new String(“abc”);创建了几个对象,常量池有abc字段是1个,常量池没有"abc"字段则是2个。
    (2)String str=“abc”;创建了几个对象(如果常量池里面已经有对象了就是0个。如果没有就是1个);
    (3)new String(“abc”).intern();创建了几个对象(如果常量池里面已经有该字符串对象了就是1个,如果没有就是两个)


    ————————————————

    原文链接:https://blog.csdn.net/guoxiaolongonly/article/details/80425548

  • 相关阅读:
    ORACLE的程序包1程序包的基
    JAVA中方法重载,方法覆盖,方法继承等小结
    使用DBMS_JOB包创建ORACLE定时任务
    linux shell 中判断语句
    oracle direction目录
    Java加载类的加载顺序
    Struts2非常简单实用的身份验证功能
    关于ListView优化的一点心得
    使用webview将网页打包成apk
    关于android下的冒烟测试
  • 原文地址:https://www.cnblogs.com/diandianquanquan/p/11442018.html
Copyright © 2011-2022 走看看