String的intern()方法详解

1、先看一个例子

String str1 = new String("A")+ new String("B");
System.out.println(str1 == str1.intern());
System.out.println(str1 == "AB");

JDK1.8的输出结果:

true
true

上边例子加一行代码其余不变:

String str2 = "AB";
String str1 = new String("A")+ new String("B");
System.out.println(str1 == str1.intern());
System.out.println(str1 == "AB");

JDK1.8的输出结果:

false
false

只是定义了一个str2,应该和str1没有什么关系吧,但是为什么会影响到最终的结果呢?
这时候就需要我们深入了解intern()方法了!!!

2、深入了解intern()方法

JDK1.6以及以前版本中,常量池是放在 Perm 区(属于方法区)中的,熟悉JVM的话应该知道这是和堆区完全分开的。JDK1.7后,常量池被放入到堆空间中,这导致intern()函数的功能不同,具体怎么个不同法,且看看下面例子,分析图是直接粘贴过来的:

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
 
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

输出结果

JDK1.6以及以下:false false
JDK1.7以及以上:false true

再分别调整上面代码2.3行、7.8行的顺序:

String s = new String("1");
String s2 = "1";
s.intern();
System.out.println(s == s2);
 
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);

输出结果:

JDK1.6以及以下:false false
JDK1.7以及以上:false false
2.1、JDK1.6分析

JDK1.6及以下版本intern()的作用:检查常量池里是否存在这个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把这个字符串添加到常量池中,然后再返回它的引用。
在这里插入图片描述
先看第一段代码:

  • String s = new String("1")会在常量池中创建常量“1”,堆中创建一个String对象指向常量池的“1”,栈中的s指向堆中的String对象
  • s.intern()会返回常量池中的地址,在这里没有什么意义
  • String s2 = "1"s2指向常量池中的“1”
  • System.out.println(s == s2)常量池中地址和堆中的地址是不一样的所以为false
  • String s3 = new String("1") + new String("1")其中new String("1")创建和第一行s创建是一样的。字符串的拼接用到了StringBuilder,这里不多做介绍。最终会新创建一个存放“11”的String对象(注意这个时候的“11”是在堆内存中),s3指向堆中的String对象
  • s3.intern()去常量池查找“11”发现不存在,将“11”复制到常量池中
  • String s4 = "11"s4会指向常量池中的“11”
  • System.out.println(s3 == s4)常量池中地址和堆中的地址是不一样的所以为false
    第二段代码和第一段基本相同,只有s3.intern()由于在String s4 = "11"之后执行,所以会在常量池找到“11”,所以不会进行复制
2.1、JDK1.7+分析

JDK1.7以及以上的版本intern()的作用:检查常量池里是否存在这个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把这个字符串在堆内存中的地址添加到常量池中,然后再返回这个地址。
先看第一段代码的情况:
在这里插入图片描述

  • String s = new String("1")会在常量池中创建常量“1”,堆中创建一个String对象指向常量池的“1”,栈中的s指向堆中的String对象
  • s.intern()会返回常量池中的地址,在这里没有什么意义
  • String s2 = "1"s2指向常量池中的“1”
  • System.out.println(s == s2)常量池中地址和堆中的地址是不一样的所以为false
  • String s3 = new String("1") + new String("1")其中new String("1")创建和第一行s创建是一样的。字符串的拼接用到了StringBuilder,这里不多做介绍。最终会新创建一个存放“11”的String对象(注意这个时候的“11”是在堆内存中),s3指向堆中的String对象
  • s3.intern()去常量池查找“11”发现不存在,将“11”在堆内存中的地址放到常量池中
  • String s4 = "11""11"在常量池中存在,所以s4会指向常量池中“11”对应的引用地址(也就是s3的地址)
  • System.out.println(s3 == s4)s3和s4中的地址是一样的所以为true

再看第二段代码:
在这里插入图片描述

  • String s = new String("1")会在常量池中创建常量“1”,堆中创建一个String对象指向常量池的“1”,栈中的s指向堆中的String对象
  • String s2 = "1"s2指向常量池中的“1”
  • s.intern()会返回常量池中的地址,在这里没有什么意义
  • System.out.println(s == s2)常量池中地址和堆中的地址是不一样的所以为false
  • String s3 = new String("1") + new String("1")其中new String("1")创建和第一行s创建是一样的。字符串的拼接用到了StringBuilder,这里不多做介绍。最终会新创建一个存放“11”的String对象(注意这个时候的“11”是在堆内存中),s3指向堆中的String对象
  • String s4 = "11"在常量池创建“11”,s4指向常量池
  • s3.intern()由于“11”已经存在,会返回常量池中的地址,在这里没有什么意义
  • System.out.println(s3 == s4)常量池中地址和堆中的地址是不一样的所以为false

3、总结

看完这些再看开篇时候的例子就很清楚了吧。最后总结以下:
intern()的作用:检查常量池里是否存在这个字符串,如果存在,就返回池里的字符串;如果不存在,JDK1.6及以下版本会把这个字符串添加到常量池中,JDK1.7以及以上的版本则把这个字符串在堆内存中的地址添加到常量池中。

posted @ 2022-11-11 10:23  路暝月  阅读(87)  评论(0编辑  收藏  举报  来源