在深入理解Java虚拟机中有以下例子:
public static void main(String[] args) { 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); }
这段代码在JDK6中运行,会得到两个false,而在JDK7中运行,会得到一个true和一个false。
产生差异的原因是,在JDK6中,intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在Java堆上,所以必然不可能是同一个引用,结果将返回false。
而JDK7(以及部分其他虚拟机,例如JRockit)的intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返回false,这是因为“java”这个字符串在执行String-Builder.toString()之前就已经出现过了,字符串常量池中已经有它的引用,不符合intern()方法要求“首次遇到”的原则,“计算机软件”这个字符串则是首次出现的,因此结果返回true。
-----------------------------------------------------------------------------------------------------
这里看看JDK1.8中对于intern方法的描述:
/** * 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();
1.一个初始为空的字符串池被String类私密地维护;
2.当stringObj.intern()被调用时,如果这个字符串池中已经包含了一个与stringObj相等(equals)的字符串时,返回这个池子里的字符串(地址);如果池子里没有,那么在池子里加入这个字符串,并返回这个stringObj(地址);
3.如果两个字符串equals,那么两个字符串的intern()也是相等的
-----------------------------------------------------------------------------------------------------
public class TestStringIntern { public static void main(String[] args) { String string1 = "计算机世界"; String string2 = "计算机世界"; String string3 = new String("计算机世界"); System.out.println(string1 == string2); System.out.println(string1 == string3); System.out.println(string1 == string3.intern()); System.out.println(string1 == string3); } }
// true
// false
// true
// false
有了第一部分与第二部分的了解,这个内容不难理解。
public class TestStringIntern { public static void main(String[] args) { String string1 = new StringBuilder("计算机世界").toString(); System.out.println(string1.intern() == string1);
String string2 = new String("软件世界");
System.out.println(string2.intern()==string2);
}
}
//false
//false
想想这里是为什么。(“计算机世界”这个字符串已经出现过1次了,那么在string1.intern时返回的是字符串池中的地址,而不是string1的地址)。string2原理相同。
public class TestStringIntern {
public static void main(String[] args) {
String string1 = new StringBuilder("计算机").append("世界").toString();
System.out.println(string1.intern() == string1);
System.out.println(string1.intern() == string1);
String string2 = "计算机世界";
System.out.println(string1 == string2);
}
}
//true
//true
//true
本质上,字符串池中存放的是第一次出现时,进入字符串池中这个字符串的地址。所以只要是与("计算机世界")这个字符串值相等的对象,在invoke intern这个方法时,返回的都是string1的地址。这里的关键是要判断出,什么时候这个字符串第一次出现的,也就是进入字符串池的时机,只要准确把握了时机,也就不难理解这些题目的各种变种。