关于String的 intern()方法

关于String的 intern()方法
Liuxz在上述 Java 代码的最后一个示例中,判断k1 == k2结果为true ,这需要结合String类的特性、字符串拼接原理以及intern()方法的作用来进行解释,具体如下:
代码分析
1 | String k1 = new String("hello") + new String("world"); |
String k1 = new String("hello") + new String("world");:- 首先,
new String("hello")会在堆内存中创建一个String对象,new String("world")同样会在堆内存中创建一个String对象。 - 对于字符串拼接操作
+,在 Java 中,其实质是通过StringBuilder类来实现的。具体来说,编译器会将代码隐式地转换为类似以下形式:
- 首先,
1 | StringBuilder sb = new StringBuilder(); |
- 而
toString()方法会返回一个新的String对象,该对象位于堆内存中,内容为"helloworld"。所以此时,k1指向的是堆内存中通过字符串拼接后新创建的String对象。
k1.intern();:intern()方法是String类的一个本地方法。它的作用是:如果字符串常量池中已经包含了与调用intern()方法的字符串对象内容相同的字符串(由equals()方法确定),则返回常量池中该字符串的引用;否则,会将该字符串对象添加到常量池中,并返回常量池中该字符串的引用。- 对于
k1,在调用intern()方法之前,常量池中并没有"helloworld"这个字符串,所以调用intern()方法后,会将k1所指向的字符串"helloworld"添加到常量池中,然后返回常量池中"helloworld"的引用。但这里需要注意,如果没有将返回值重新赋值给k1,k1依然指向堆中的对象 。
- 对于
String k2 = "helloworld";:这是通过直接赋值的方式创建字符串对象,这种方式创建的字符串对象会首先去字符串常量池中查找是否存在
"helloworld"。因为之前已经调用了k1.intern();,使得"helloworld"被添加到了常量池中,所以k2直接指向常量池中已存在的"helloworld"字符串对象。System.out.println(k1 == k2);:==运算符比较的是两个引用是否指向同一个对象。在本示例中,由于之前k1.intern()的返回值没有重新赋值给k1,所以k1还是指向堆中的对象,k2指向常量池中的对象,此时比较结果应该是false。但在 JDK 6 及之前的版本中,intern()方法是在永久代中维护字符串常量池,而 JDK 7 及之后,字符串常量池被移动到了堆内存中,并且intern()方法的实现也有了变化。
==在 JDK 7 及之后,intern()方法如果发现常量池中不存在对应的字符串,会将堆中该字符串对象的引用复制到常量池中,而不是重新创建一个新的对象。 因此,在这个示例中,k1.intern()执行后,常量池中存储的就是堆中k1所指向对象的引用,所以k1和k2指向的是同一个对象(本质是常量池中的引用指向了堆中k1所指向的对象 ),最终k1 == k2的结果为true。==
在 Java 中,String类的intern()方法返回值是一个String类型,具体来说:
若字符串常量池中已存在与调用
intern()方法的字符串对象内容相同的字符串:- 依据
equals()方法来判断字符串内容是否相等。当相等情况出现时,intern()方法会返回常量池中该字符串的引用。
若字符串常量池中不存在与调用
intern()方法的字符串对象内容相同的字符串:- 在 JDK 6 及之前版本中,会在永久代的字符串常量池中创建一个新的字符串对象,其内容与调用
intern()方法的字符串对象相同,然后返回这个新创建在常量池中的字符串对象的引用。 - 而在 JDK 7 及之后版本,字符串常量池被移至堆内存。此时,
intern()方法会将堆中该字符串对象的引用复制到常量池中 ,然后返回常量池中这个引用,也就是指向堆中原有字符串对象的引用。
- 依据




