美文网首页
不同 JDK 的 String.Intern ( ) 的不同之处

不同 JDK 的 String.Intern ( ) 的不同之处

作者: 得瑟的小蚂蚁 | 来源:发表于2018-11-21 11:49 被阅读28次

最近在读 《深入理解java虚拟机》,总结一下 String.intern 知识点,引入书中的一个题目:

    public class RuntimeConstantPoolOOM {
            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);

              }

        }

这段代码在JDK 1.6中运行,会得到两个false,而在JDK1.7中运行,会得到一个true和一个false。产生差异的原因是:在JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,而StringBuildler创建的字符串实例在Java堆上,所以必然不是一个引用,将返回false。而JDK1.7中(以及部分其他虚拟机,例如JRockit)的intern()实现不会再 复制实例,只是在常量池中记录首次出现实例的引用,因此intern()返回的引用和由StringBuilder创建的字符串实例是同一个;对str2比较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用,不符合首次出现的规则,而"计算机软件"这个字符串则是首次出现的,因为返回true;

下面引入几幅图对 intern 方法作如下总结:
  • new String 都是在堆上创建字符串对象。当调用 intern() 方法时,JDK1.6中编译器会将字符串添加到常量池中,并返回指向该常量的引用。
image image
  • 通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。
image
  • 常量字符串的“+”操作,编译阶段直接会合成为一个字符串。如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用。
  • 对于final字段,编译期直接进行了常量替换(而对于非final字段则是在运行期进行赋值处理的)。
    final String str1 = ”ja” ;

    final String str2 = ”va” ;

    String str3 = str1+str2 ;

在编译时,直接替换成了 String str3 = ”ja” + ”va”,根据第三条规则,再次替换成 String str3=”JAVA” 。

  • 常量字符串和变量拼接时(如:String str3 = baseStr + “01” ;)会调用 stringBuilder.append() 在堆上创建新的对象。
  • JDK 1.7 后,intern 方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。

另外参考 关于String.intern()和new StringBuilder("").append("").toString();

先运行这个代码 ①

    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" 这个字符串常量没有被预先加载到常量池中 。
但是运行结果却是 false .

解释如下 :上面的 ① 代码等价于下面的代码

    String a = "ni";

    String b = "hao";
    
    String str3 = new StringBuilder(a).append(b).toString();
    
    System.out.println(str3==str3.intern());

很容易分析出 :

“nihao” 最先创建在堆中 str3.intern() 然后缓存在字符串常连池中 运行结果为 true .

代码 ② 等价于

    String a = "nihao";

    String str3 = new StringBuilder(a).toString();
    
    System.out.println(str3==str3.intern());

很容易分析出:

“nihao” 最先创建在常量池中, 运行结果为false.

new String()和new StringBuilder()同样的原理,由此对于一道 经典的Java面试题

在Java中,new String("hello")这样的创建方式,到底创建了几个String对象 ?

题目下答案众说纷纭,有说1的有说2的,我觉得各有各的道理,如果常量池中有“hello”的字符串,当然只会创建1个,如果没有则会创建2个;

new String ("hello")相当于如下代码:

    String temp = "hello";  // 在常量池中

    String str = new String(temp); // 在堆上

相关文章

  • 不同 JDK 的 String.Intern ( ) 的不同之处

    最近在读 《深入理解java虚拟机》,总结一下 String.intern 知识点,引入书中的一个题目: 这段代...

  • String.intern()方法作用

    String.intern()方法设计的初衷:重用字符串,节省内存 JDK1.6中使用String.intern(...

  • 【Java必修课】String.intern()原来还能这么用(

    1 简介 String.intern()是JDK一早就提供的native方法,不由Java实现,而是底层JVM实现...

  • 不同之处

    不同之处 国庆节和中秋节不同,中秋节我们只会吃月饼,而国庆节呢? ...

  • 不同之处

    我和爱人的不同之处是他的性格慢,我的性格快急,有些时候难免有些摩擦,年轻的时候说话不留情面,没有把尊重看的多重要,...

  • 不同之处

    我们经常听这样一句话,如果一个人说你,你可能没有错。如果十个人说你,你就得好好反思自己是不是有错了。 有句禅话讲,...

  • 不同之处

    冬天的夜晚总是来的很早,我们两人又从小镇回到县城家中,家中相当一部分的钱都捐给路费了,来回跑,花钱遭罪,不...

  • 不同之处

    和别人发生争执之时,必是你身体紧绷,神经不受控制的时候。 可能都没有意识到自己身体紧绷,当时的攻击力很强,语气极其...

  • $@与$*的不同之处

    $@与$*的不同之处 $*: 把我们传递给脚本的参数全部合为一个字节,当成一个字符串或者参数来使用。 $@: 把我...

  • 做人的不同之处

    人是有质地的,可甄别的,与世间的万物一样。但有几个细节却隐藏在几乎不被人注意的外表下面,你可以通过这样的细节看出一...

网友评论

      本文标题:不同 JDK 的 String.Intern ( ) 的不同之处

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