public static void main(String[] args) {
String s1 = new String("aa");
s1.intern();
String s2 = "aa";
System.out.println(s1 == s2);
//wrong in JDK1.6 but true in JDK1.8
String str1 = new String("str") + new String("01");
str1.intern();
String str2 = "str01";
System.out.println(str1 == str2);
}
我用JDK1.8运行上面的代码,我认为结果会得到两个“谬误”,因为在我看来,很明显s1& str1位于堆中,s2& str2是在字符串池中实现的,但是我得到了一个“false”和一个“true”。 问题来了:是什么导致了“真实”?
以上是原始问题。 现在要确定这个问题远不是这些被称为重复的问题,我想谈谈我的新发现:代码的第二部分使用JDK1.6获得“错误”结果,而使用JDK1.8获得“真实”结果。一些博客称,在JDK1.7发布后,intern()的行为发生了变化。
如果池中没有包含等于此String对象的字符串,则此String对象将不添加到池中,并且对此String对象的引用将添加到池中。这意味着池中的引用将被分配给位于其他位置的字符串对象(如堆),并且下一次文本字符串的初始化等于早期的字符串对象也将被分配给字符串对象。这正好描述了代码part2关于“真实”结果。
这个理论确实可以用来解释上面代码的结果。但很明显,该理论不属于intern()doc所包含的内容,这在JDK6 / 8 API中几乎相同。
现在的问题是:对于JDK1.6中相同代码的不同结果是否有更好的解释? JDK 1.8?我上面提到的理论究竟是真正发生的事情吗?
答案 0 :(得分:20)
首先让我们看看String.intern()
的{{3}}:
调用实习方法时,如果池已包含 字符串等于此String对象由equals(Object)确定 方法,然后返回池中的字符串。 否则,这个 String对象添加到池以及对此String的引用 返回对象。
然后让我们看看你的代码:
final String s1 = new String("aa");
s1.intern();
final String s2 = "aa";
System.out.println(s1 == s2);
在此创建 3 String 对象。 字符串文字 "aa"
,添加到字符串池,new String("aa")
,这是一个副本构造来自"aa"
,但是不同的实例,第二个字符串文字 "aa"
,取自池再次(因此它与第一个"aa"
的实例相同)。当您调用intern()
时,字符串池已经有一个 String 等于s1
(从中构造的文字"aa"
) ,因此返回"aa"
,但您不使用它,s1
仍然是另一个实例。因此s1 == s2
为false
。
final String str1 = new String("str")+new String("01");
str1.intern();
final String str2 = "str01";
System.out.println(str1 == str2);
在此创建 6 String 对象。文字"str"
和"01"
,放入池。然后从这些文字创建两个副本。然后是这两个字符串的串联,它将是str1
。此并置不会立即放入池,但是当您在intern()
上调用str1
时,它将被置于池中,因此{{ 1}}本身现在在池中。因此,当您将str1
创建为字符串文字 str2
时,它将从池中获取,因此与{{1}相同}}
答案 1 :(得分:1)
String.intern()返回字符串对象的规范表示,因此如果要在字符串池中引用该规范表示,则需要指定它。
String s1 = new String("aa");
s1.intern();
此处s1
仍然是您声明的对象,并且位于堆中。
你需要做
String s1Itern = s1.intern();
如果你想要文字字符串
s1 == s2 is false
s1Itern == s2 is true
对于第二种情况,Max's Answer很好地解释了它。