在周志明的《深入理解java虚拟机》,2nd,p57中有一部分代码
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
对照着intern()看了api源码上的javadoc注释
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
个人理解是:
如果调用intern之前字符串已存在,返回字符串对象,否则返回字符串引用。“首次出现”返回引用。
讲到在jdk 7之前的版本中,intern() 会把首次遇到的String实例复制到永久代中,返回的是永久代中String实例的引用。由StringBuilder创建的String实例在堆上,所以不是同一个引用,返回false。
按照jdk历史版本的内存区域分布图,得知jdk 7之前的执行结果为
false
false
在 jdk 6中,intern() 会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回也是永久代里面字符串实例引用。由 StringBuilder 创建的字符串对象实例在 java 堆上,所以必然不是同一个引用,结果返回 false。
jdk 7及以后的执行结果为
true
false
jdk 7中,intern() 实现就不需要 copy 字符串的实例到永久代了,既然字符串常量池已经移到 java 堆中,只需要在常量池里记录一下首次出现的实例引用即可,因此 intern() 返回的引用和 StringBuilder 创建的那个字符串实例就是同一个。
但是不太理解书籍里讲到的
“java”这个字符串在执行StringBuilder的toString()之前已经出现过,字符串常量池已经有它的引用了,不符合“首次出现”的原则。
感觉这里作者埋坑了,在看到3rd版里讲到这个地方,说读者有这个疑问,给了注释讲到说是在sun.misc.Version中已经存在,提到了知乎里的作者RednaxelaFX在https://www.zhihu.com/question/51102308/answer/124441115
里做了讲解, 讲的很好。