美文网首页Java 杂谈Java学习笔记技术干货
String源码中的intern()方法使用分析

String源码中的intern()方法使用分析

作者: 夏广成 | 来源:发表于2017-06-15 19:16 被阅读101次

    intern()该方法的注释是

    When the intern method is invoked, if the pool already contains a
    string equal to this {@code String} object as determined by
    the {@link #equals(Object)} method, then the string from the pool is
    returned. Otherwise, this {@code String} object is added to the
    pool and a reference to this {@code String} object is returned.

    根据注释我们可以理解,这个方法是将一个字符串拿到常量池中寻找,如果有这个字符串,那么就返回这个字符串,如果没有,就把这个字符串放到常量池中,然后返回这个字符串。

    第一种情况

    在java不同版本中常量池的所在位置不同。jdk6.0的常量池是存放在堆内存的perm区中,默认长度为1009,在jdk7.0中常量池从perm区域转移到正常的堆内存中。在jdk8.0中,perm区取消,新建立了一个元区域。
    如果我们先不使用intern,我们来看看下面这样一种情况

    String str2=new String("abc");
    String str1="abc";
    System.out.println(str1==str2);
    

    上面输出的结果是

    fasle
    
    

    这个大家应该都知道,str2是一个堆内存中的对象,而str1指向的却是常量池中的地址,所以二者不等。通过双引号直接生成的字符串都会放在常量池。这时我们引入intern。上面的代码变为下面的代码

    String str2=new String("abc");
    str2.intern();
    String str1="abc";
    System.out.println(str1==str2);
    
    

    上面的答案依然是

    false
    

    那究竟有什么变化呢?当代码执行完第一句,一共生成了两个对象,一个是str2,一个是“abc",其中str2是在堆内存中,"abc"是在常量池中。这时执行第二句,intern是到常量池中寻找,发现“abc"已经在常量池中了,因此就不用再往里面添加了(实际上这一行代码在这组代码中无用)。第三句代码是在栈内存中生成一个执行常量池中"abc"的变量。接下来我们比较在堆内存中的str2地址和str1指向的常量池中的“abc"的地址,因此不相等。我们在前面的解释中说到intern实际上会返回常量池中的string常量的引用。那么我们通过一个变量来接受这个返回值,然后将这个返回值和str1进行比较。

    String str2=new String("abc");
    String str3 = str2.intern();
    String str1="abc";
    System.out.println(str1==str3);
    
    

    那么结果就是

    true
    

    这就和我们的理解一致了。

    第二种情况

    接下来我们再看看下面的代码

    String str6=new String("a")+new String("a");
    String str5="aa";
    System.out.println(str5==str6);
    

    这个返回值明显也是

    false
    

    第一行代码一共生成了四个对象,两个在堆内存中的new String("a"),常量池中存在的一个"a",以及一个在堆内存中的“aa",也就是str6。这个时候"aa"也就是str6,还没有放到常量池中。接下来代码执行到第二句,在常量池中生成了"aa"。这是我们来比较,肯定str5和str6不一致。那么我们换一种情况,加入intern

    String str6=new String("a")+new String("a");
    str6.intern();
    String str5="aa";
    System.out.println(str5==str6);
    

    根据上面的分析我们知道第一行代码生成了四个对象,但是"aa"并没有放入常量池,但是intern方法,是会将"aa" 放入常量池的(我们在jdk7.0中说到perm调整到堆内存中,就是说当堆内存中已存在一个字符串时,我们在perm中直接保存这个堆内存地址就可以了。),实际上此时放入常量池的是一个堆内存中"aa"的地址。这时再执行第三行代码,str5发现常量池中已经存在"aa"了,并且这个"aa"是保存了堆内存中"aa"的地址,于是str5虽然也指向了常量池中的"aa",实际上还是指向了堆内存中的"aa"。结果如我们分析的一样

    true
    

    这里比较难理解的就是两个字符串对象的连接之后,并没有将结果直接放在常量池中。并且当我们手动调用intern的时候,在常量池中并不是新生成一个字符串,而是生成了一个指向在堆内存中的这个字符串的地址。这里引用一张来自美团技术团队的图片

    image.png

    相关文章

      网友评论

        本文标题:String源码中的intern()方法使用分析

        本文链接:https://www.haomeiwen.com/subject/flhuqxtx.html