zoukankan      html  css  js  c++  java
  • String.intern()初探

    今天在看深入理解java虚拟机关于String.intern()返回引用测试时,看得我有点云里雾里

    public static void main(String[] args) {
            String str1=new StringBuilder("计算机").append("软件").toString();
            System.out.println(str1.intern()==str1);
            String str2=new StringBuilder("ja").append("va").toString();
            System.out.println(str2.intern()==str2);
            
        }

    在JDK1.6中运行,会得到两个false。这个不难理解:

    在JDK1.6中,intern()方法会把首次遇到的字符串实例复制到常量池中(堆上的副本并不是堆对象的地址),返回的是常量中这个串实例的引用,而由StringBuilder创建的字符串实例在堆上

    固然并不是一个引用,返回两个false

    但在JDK1.7中,得到的答案则是一个true,一个false

    这是因为,JDK1.7+中,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。

    所以第一个true则很好解释。str1.intern是查询"计算机软件"这个串是否存在于常量池

    很好显然是不存在,那么则将“计算机软件”这个串在堆上的引用(地址)放入到常量池中 并返回这个引用(地址)

    所以为ture

    那么第二个false应该如何解释呢?难道之前常量池中已经存放过java这个串的堆地址了吗

    最后在网上的博客上找到了答案(最开始以为是StringBuilder的原因,查看了一下StringBuilder的源码,发现里面没有加载字符串常量,网上也找了关于intern()的,发现都只是对比JDK 1.6和JDK 1.7之间上面代码的运行结果比较,后来找了许久,终于找到一篇关于[String.intern()探究]: <http://baijiahao.baidu.com/s?id=1568390319555291&wfr=spider&for=pc>的文章,发现要去查看System的源码)

    java虚拟机会自动调用System类

    /* register the natives via the static initializer.
     *
     * VM will invoke the initializeSystemClass method to complete
     * the initialization for this class separated from clinit.
     * Note that to use properties set by the VM, see the constraints
     * described in the initializeSystemClass method.
     */
    在System类中的注释可以知道,调用了initializeSystemClass方法,在此方法中调用了Version对象的init静态方法
    sun.misc.Version.init();
    因此sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化。
    查看Version类定义的私有静态字符串常量如下:
    private static final String launcher_name = "java";
    private static final String java_version = "1.7.0_51";
    private static final String java_runtime_name = "Java(TM) SE Runtime Environment";
    private static final String java_runtime_version = "1.7.0_51-b13";
    在初始化Version类时,对其静态常量字段根据指定的常量值做默认初始化,所以"java"被加载到了字符串常量池中,修改上面代码使字符串值为上面常量中的任意一个都会返回false。
    String str2=new StringBuilder("1.7.0").append("_51").toString();
    System.out.println(str2.intern()==str2);
    View Code

    那么还有一个疑问

    先运行这个代码
    String str3 = new StringBuilder("ni").append("hao").toString();
    System.out.println(str3==str3.intern());
    通过上面的解释,运行结果为true.
    

     

    再运行这个代码
    String str3 = new StringBuilder("nihao").toString();
    System.out.println(str3==str3.intern());
    其结果是什么?应该还是true吧,毕竟通过上一个运行结果可以知道"nihao"这个字符串常量没有被预先加载到常量池中。

    这两个又有上面区别呢?为什么答案不一样呢

    第一个:常量池中实际上存的是“ni”和“hao”的堆内存引用

    而str3的“nihao”则为.toString的堆内存 而未存放到常量池中,所以str.intern则是吧str3的堆内存放入常量池,故str3==str3.intern()

    第二个:常量池在“nihao”的时候就把“nihao”的堆内存放入了常量池,而.toString又会为StringBuilder生成另一个副本命名为str3

    str3(副本)的地址并不与str3.intern()第一行的“nihao”地址一样   固为false

    str3

  • 相关阅读:
    Android-通过SlidingPaneLayout高仿微信6.2最新版手势滑动返回(一)
    B树
    nyoj448 寻找最大数
    IT痴汉的工作现状22-由Dalvik虚拟机引发的口水战
    POJ 3221 Diamond Puzzle.
    CMDBuild安装及webservice接口的获取
    安卓dex 文件结构简要说明
    安装RPM包或者安装源代码包
    Java程序性能优化技巧
    [Sqlite]--&gt;数据迁移备份--从低版本号3.6.2到高版本号3.8.6
  • 原文地址:https://www.cnblogs.com/Mr-BING/p/12321920.html
Copyright © 2011-2022 走看看