先看下面一段代码:
public static void main(String[] args) {
String str1 = new String("ja") + new String("va");
String intern1 = str1.intern();//line1
String s1 = "java";//line2
System.out.println("str1==intern1:" + (str1 == intern1));//false
System.out.println("s1==intern1:" + (s1 == intern1));//true
System.out.println("s1==str1:" + (s1 == str1));//false
System.out.println("--------------------------");
String str2 = new String("爪") + new String("哇");
String intern2 = str2.intern();//line3
String s2 = "爪哇";//line4
System.out.println("str2==intern2:" + (str2 == intern2));//true
System.out.println("s2==intern2:" + (s2 == intern2));//true
System.out.println("s2==str2:" + (s2 == str2));//true
}
初看运行结果,我也觉得相当困惑,为什么同样的代码结构返回结果不一样的,后来查阅资料才知道,对于new出来的字符串,调用intern()方法时,intern()方法会把首次遇到的字符串,在常量池中记录首次出现的实例的引用,也就是说,因此intern()返回的引用和new创建的那个字符串的实例是同一个,故而str2==intren2,因为"爪哇"在字符串常量池中原本是不存在的,而"java"在字符串常量池中原本是存在的,所以intern2返回的就是str2的引用,而intern1返回的是原本在字符串常量池中存在的引用。s1直接用双引号赋值,自然就是字符串常量池中的引用,s2同理。
下面调换上述代码中line1和line2的位置,line3和line4位置。
运行结果就变了("爪哇"的结果的变了)。
public static void main(String[] args) {
String str1 = new String("ja") + new String("va");
String s1 = "java";
String intern1 = str1.intern();
System.out.println("str1==intern1:" + (str1 == intern1));//false
System.out.println("s1==intern1:" + (s1 == intern1));//true
System.out.println("s1==str1:" + (s1 == str1));//false
System.out.println("--------------------------");
String str2 = new String("爪") + new String("哇");
String s2 = "爪哇";
String intern2 = str2.intern();
System.out.println("str2==intern2:" + (str2 == intern2));//false
System.out.println("s2==intern2:" + (s2 == intern2));//true
System.out.println("s2==str2:" + (s2 == str2));//false
}
为什么呢,这是因为 String s2 = "爪哇";在String intern2 = str2.intern();之前执行,在调用intern之前调用了 String s2 = "爪哇",而调用 String s2 = "爪哇"之前字符串常量池中是没有 "爪哇",故而在字符串常量池新创建 "爪哇"的引用,并且指向s2,而此时再调用str2的intern方法,字符串常量池中已存在 "爪哇",直接返回了 "爪哇"的引用给intern2,也就是s2和intern2指向的同一个引用。还是回到了最初那句话,调用intern()方法时,intern()方法会把首次遇到的字符串,在常量池中记录首次出现的实例的引用。
再看下面一段代码的运行的结果,理解会更加深刻:
String str3_1 = new String("爪") + new String("哇");
String str3_2 = new String("爪") + new String("哇");
System.out.println(str3_1 == str3_2);//false
String intern1 = str3_1.intern();//line5
String intern2 = str3_2.intern();//line6
System.out.println(intern1 == intern2);//true
System.out.println(intern1 == str3_1);//true
str3_1和str3_2在堆中分别创建两个对象,两个直接比较肯定不同,第一个false很好理解。随后调用str3_1的intern()方法,字符串常量池中不存在"爪哇",将str3_1在堆中的地址引用复制到字符串常量池,而随之调用str3_2的intern()方法,此时字符串常量池中已存在"爪哇",直接将该引用返回给intern2,也就是说str3_1、intern1、intern2指向的是同一块地址。
而将line5、line6调换位置,结果可想而知,此时就是str3_2、intern1、intern2指向的是同一块地址了。