关于String的 intern()方法

在上述 Java 代码的最后一个示例中,判断k1 == k2结果为true ,这需要结合String类的特性、字符串拼接原理以及intern()方法的作用来进行解释,具体如下:

代码分析

1
2
3
4
String k1 = new String("hello") + new String("world");
k1.intern();
String k2 = "helloworld";
System.out.println(k1 == k2); // true
  1. String k1 = new String("hello") + new String("world"); :
    • 首先,new String("hello")会在堆内存中创建一个String对象,new String("world")同样会在堆内存中创建一个String对象。
    • 对于字符串拼接操作+ ,在 Java 中,其实质是通过StringBuilder类来实现的。具体来说,编译器会将代码隐式地转换为类似以下形式:
1
2
3
4
StringBuilder sb = new StringBuilder();
sb.append(new String("hello"));
sb.append(new String("world"));
String k1 = sb.toString();
  • toString()方法会返回一个新的String对象,该对象位于堆内存中,内容为"helloworld"。所以此时,k1指向的是堆内存中通过字符串拼接后新创建的String对象。
  1. k1.intern(); :

    intern()方法是String类的一个本地方法。它的作用是:如果字符串常量池中已经包含了与调用intern()方法的字符串对象内容相同的字符串(由equals()方法确定),则返回常量池中该字符串的引用;否则,会将该字符串对象添加到常量池中,并返回常量池中该字符串的引用。

    • 对于k1,在调用intern()方法之前,常量池中并没有"helloworld"这个字符串,所以调用intern()方法后,会将k1所指向的字符串"helloworld"添加到常量池中,然后返回常量池中"helloworld"的引用。但这里需要注意,如果没有将返回值重新赋值给k1 ,k1依然指向堆中的对象 。
  2. String k2 = "helloworld"; :

    这是通过直接赋值的方式创建字符串对象,这种方式创建的字符串对象会首先去字符串常量池中查找是否存在"helloworld"。因为之前已经调用了k1.intern(); ,使得"helloworld"被添加到了常量池中,所以k2直接指向常量池中已存在的"helloworld"字符串对象。

  3. System.out.println(k1 == k2); :

    ==运算符比较的是两个引用是否指向同一个对象。在本示例中,由于之前k1.intern()的返回值没有重新赋值给k1 ,所以k1还是指向堆中的对象,k2指向常量池中的对象,此时比较结果应该是false 。但在 JDK 6 及之前的版本中,intern()方法是在永久代中维护字符串常量池,而 JDK 7 及之后,字符串常量池被移动到了堆内存中,并且intern()方法的实现也有了变化。

==在 JDK 7 及之后,intern()方法如果发现常量池中不存在对应的字符串,会将堆中该字符串对象的引用复制到常量池中,而不是重新创建一个新的对象。 因此,在这个示例中,k1.intern()执行后,常量池中存储的就是堆中k1所指向对象的引用,所以k1k2指向的是同一个对象(本质是常量池中的引用指向了堆中k1所指向的对象 ),最终k1 == k2的结果为true==


在 Java 中,String类的intern()方法返回值是一个String类型,具体来说:

  • 若字符串常量池中已存在与调用intern()方法的字符串对象内容相同的字符串

    • 依据equals()方法来判断字符串内容是否相等。当相等情况出现时,intern()方法会返回常量池中该字符串的引用。

    若字符串常量池中不存在与调用intern()方法的字符串对象内容相同的字符串

    • 在 JDK 6 及之前版本中,会在永久代的字符串常量池中创建一个新的字符串对象,其内容与调用intern()方法的字符串对象相同,然后返回这个新创建在常量池中的字符串对象的引用。
    • 而在 JDK 7 及之后版本,字符串常量池被移至堆内存。此时,intern()方法会将堆中该字符串对象的引用复制到常量池中 ,然后返回常量池中这个引用,也就是指向堆中原有字符串对象的引用。