关于String.intern()返回测试的详细解释以及思考

在阅读 周志明《深入理解Java虚拟机时》,遇到了一个经典的案例,理解时花费了较多时间,这里分享一下自己的思考。

案例

  1. public void test01() {
  2. String str1=new StringBuilder("计算机").append("软件").toString();
  3. System.out.println(str1.intern()==str1);//true
  4. String str2=new StringBuilder("ja").append("va").toString();
  5. System.out.println(str2.intern()==str2);//false
  6. }

预备知识

在JDK 7以及以上的版本中,由于字符串常量池和String对象都处在 堆 上,因此字符串常量池储存的是对堆中字符串对象的引用。

intern()方法的作用:如果字符串是第一次出现,该方法会将字符串加入字符串常量池中;如果不是第一次出现,那么会将字符串在字符串常量池中的引用返回。

原因解释

那么为什么会出现上面的现象呢?

需要注意的是,字符串在Java中也是一类特殊的对象,只不过由于它常使用作了特殊的优化(如字符串常量池)。

在第一个案例中,由于”计算机软件”是第一次出现,所以intern()方法返回的就是str1的地址。

在第二个案例中,由于”java”先前出现过,因此intern()方法返回的是先前出现的”java”字符串地址(在sun.misc.Version中被加载)

图示如下:

image.png

部分思考

  • 在阅读书本时,有过疑惑为什么要使用StringBuilder而不直接使用String?
  • 为什么特意append?
  1. public void test02() {
  2. String str1=new StringBuilder("计算机软件").toString();
  3. System.out.println(str1.intern()==str1);//false
  4. String str2=new StringBuilder("java").toString();
  5. System.out.println(str2.intern()==str2);//false
  6. }
  7. public void test03() {
  8. String str1="计算机"+"软件";
  9. System.out.println(str1.intern()==str1);//true
  10. String str2="ja"+"va";
  11. System.out.println(str2.intern()==str2);//true
  12. }

可以看出,这里是运行结果和原案例中是不同的。

要理解这个问题,需要先知道

  • 在任何地方出现的被””包裹的字符串都会被加入常量池
  • JVM会将形如”ja”+”va”的字符串自动优化为”java”再加入字符串常量池
  • StringBuilder在使用toString()方法时会产生新的字符串对象,不会从字符串常量池中获取,并且不会将生成的字符串加入字符串常量池
    参考博客

1、使用StringBuider以及append()的原因:

而原案例test01()中,通过StringBuilder的append()方法巧妙地避开了JVM会将形如”ja”+”va”的字符串自动优化为”java”再加入字符串常量池的特性,因此可以在之后调用intern()方法将字符串加入常量池。以Str1为例,会将”计算机”和”软件”分别加入字符串常量池,而不会将”计算机软件”加入常量池。

2、test02()中都为false的原因:

由于特性在任何地方出现的字符串都会被加入常量池,在test02()中先创建了String对象”计算机软件”,直接将”计算机软件”加入到字符串常量池中了,再将”计算机软件”转为StringBuild,最后再次通过toString()转为str1,这里的str1为新的字符串对象。而str1.intern()调用是返回的是”计算机软件”的地址,而不是str1的地址

3、test03()中都为true的原因:

直接使用String时,java虚拟机会自动优化,将”ja”和”va”自动合并成”java”,并且由于在字符串常量池中存在,会直接返回字符串常量池中的引用。值得指出的是,这里的str1.intern()==str1的原因和原案例中是不同的,这里的str1在创建时就被加入到字符串常量池,因此str1.intern()在这里并不会执行将str1加入常量池的行为,而是直接返回字符串常量池中队str1的引用。

因此StringBuilder和append()方法缺一不可

由于笔者也刚接触JVM,写的不好望指正


文章标签:

原文连接:https://juejin.cn/post/7115695739832926244

相关推荐