欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java深入之String.intern()

程序员文章站 2022-06-05 13:46:33
...

今天看了大神的一篇讲解intern()函数的文章,有所领悟,在此记录。

一.背景知识

要理解这个函数,很多知识是必不可少的,下面一一列出。


  1. intern()方法的作用:
    JDK1.6:根据字符串对象,检查常量池中是否存在相同字符串对象,如果不存在,在常量池中创建该字符串常量,返回该常量引用,否则直接返回已存在的常量引用。
    JDK1.7:根据字符串对象,检查常量池中是否存在相同字符串对象,如果不存在,
    创建指向堆中字符串对象的引用,返回该引用,否则直接返回已存在的常量引用。

  2. java字符串字面量,即用双引号括起来的字符串”a”,”b”等,属于在编译期就确定好的,因此会被放入常量池,并且在常量池中不会重复存在两个相同的字符串。如果有两个引用指向同一个字符串常量,那么他们实际上指向位于常量池中的同一个对象。

  3. 所有引用变量都存在与java虚拟机栈中。他们或者指向堆中的一个对象,或者指向常量池中的一个对象。

  4. 在JDK1.7以前,运行时常量池存在于方法区,而在JDK1.7开始,运行时常量池被移动到了堆区。这使得,如果调用intern()时,发现常量池中并没有该字符串常量,那么不需要再创建一个常量,而是直接创建指向堆中字符串对象的引用!相当于重用了字符串对象。
  5. new String(“a”)必定会在堆中生成一个对象,不过可能是匿名的,同时也会在常量池中生成一个常量”a”(如果a还不在常量池中的话)。
  6. “a”+”b”并不会在常量池中生成常量”ab”,只会生成”a”和”b”,因为字符串连接的结果是运行时才能确定的,编译期无法确定。最终会返回一个存在于堆中的”ab”字符串对象。
  7. 对字符串使用==,判断的是两个引用指向的地址。需要注意的是,如果指向的是常量池中的对堆字符串对象的引用,那么会使用堆字符串对象的地址进行判断!

二.案例分析

这里使用大神的案例

案例一

String str2 = "SEUCalvin";//新加的一行代码,其余不变  
String str1 = new String("SEU")+ new String("Calvin");      
System.out.println(str1.intern() == str1);   
System.out.println(str1 == "SEUCalvin"); 

返回结果:false false

首先,在JDK1.7中,常量池会生成”SEUCalvin”对象,引用str2指向常量池中”SEUCalvin”,而引用str1指向堆中”SEUCalvin”对象,这两个对象不同,虽然代表相同字符串,但是一个位于常量池,一个位于堆。另外在第二行代码执行完毕后,”SEU”,”Calvin”也会出现在常量池中。
之后调用str1.intern(),此时常量池中存在相同字符串,那么返回常量池对象的引用,显然,常量池对象和堆对象地址不同,返回false。
最后,str1指向的是堆对象,字面量”SEUCalvin”指向的地址和str1.intern()相同,都是常量池中那个字符串的地址,返回false


案例2

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

返回结果:true

首先堆中生成两个匿名字符串对象,同时常量池中生成“1”字符串,最后,s3指向一个堆中的”11”的字符串对象。
第二行代码执行时,由于常量池中并没有”11”字符串,因此常量池中生成一个指向堆中”11”字符串对象的引用。(知识点4)
由于第三行代码执行时,常量池中已存在字符串”11”的引用,所以s4也指向堆中的”11”对象。
s3和s4实际指向堆中的同一个对象,所以结果为true。


额外实验

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

返回结果:false

我看到有的资料说字符串字面量在类加载时被载入,那么这样的话似乎常量池中的字符串常量应该与相应代码被执行的次序无关。但是这段代码说明实际上并不是,执行次序很重要,如果

String s4 = "11";   

先执行,就会在常量池生成一个字符串对象,如果

s3.intern(); 

先执行,那么常量池就不会再生成一个对象,而是直接生成指向堆对象的引用。
此处略有疑惑,如有高手知晓,还望告知,十分感谢。