我试过下面的代码:
public class TestIntern {
public static void main(String[] args) {
char[] c1={'a','b','h','i'};
String s1 = new String(c1);
s1.intern();
String s2="abhi";
System.out.println(s1==s2);//true
char[] c2={'j','a','v','a'};
String sj1 = new String(c2);
sj1.intern();
String sj2="java";
System.out.println(sj1==sj2);//false
char[] c3={'J','A','V','A'};
String tj1 = new String(c3);
tj1.intern();
String tj2="JAVA";
System.out.println(tj1==tj2);//true
}
}
我尝试了很多不同的文字。
有人可以解释为什么intern()
无法按预期使用文字"java"
?为什么当文字为true
时,上述参考比较的评估结果为"java"
,除了?
答案 0 :(得分:16)
当JVM首次遇到new String(new char[] {'a', 'b', 'h', 'i'})
字符串并且您在其上调用intern()
时,您刚创建的引用将成为规范引用并存储在字符串常量池中。然后"abhi"
从常量池中拉出 - 您的规范实例已被重用。
你的问题是在程序启动之前,常量字符串池中存在文字"java"
- JVM只是将其用于某些用途。因此,在intern()
上调用new String(new char[] {'j', 'a', 'v', 'a'})
会不实习您的参考。相反,它从常量池返回预先存在的规范值,您很乐意忽略返回值。
您不应忽略返回值,而是使用它。你永远都不知道自JVM启动以来你的“绝对原创”字符串是否一直没有存在于常量池中。无论如何,所有这些都是依赖于实现的,你应该总是使用intern()
方法返回的引用,或者永远不要。不要在它们之间混合。
答案 1 :(得分:3)
answer by Petr Janeček几乎肯定是正确的(+1那里)。
真的证明很难,因为很多字符串池都驻留在JVM本身,如果没有经过调整的VM,就很难访问它。
但这里有更多的证据:
public class TestInternEx
{
public static void main(String[] args)
{
char[] c1 = { 'a', 'b', 'h', 'i' };
String s1 = new String(c1);
String s1i = s1.intern();
String s1s = "abhi";
System.out.println(System.identityHashCode(s1));
System.out.println(System.identityHashCode(s1i));
System.out.println(System.identityHashCode(s1s));
System.out.println(s1 == s1s);// true
char[] cj =
{ 'j', 'a', 'v', 'a' };
String sj = new String(cj);
String sji = sj.intern();
String sjs = "java";
System.out.println(System.identityHashCode(sj));
System.out.println(System.identityHashCode(sji));
System.out.println(System.identityHashCode(sjs));
System.out.println(sj == sjs);// false
char[] Cj = { 'J', 'A', 'V', 'A' };
String Sj = new String(Cj);
String Sji = Sj.intern();
String Sjs = "JAVA";
System.out.println(System.identityHashCode(Sj));
System.out.println(System.identityHashCode(Sji));
System.out.println(System.identityHashCode(Sjs));
System.out.println(Sj == Sjs);// true
char[] ct =
{ 't', 'r', 'u', 'e' };
String st = new String(ct);
String sti = st.intern();
String sts = "true";
System.out.println(System.identityHashCode(st));
System.out.println(System.identityHashCode(sti));
System.out.println(System.identityHashCode(sts));
System.out.println(st == sts);// false
}
}
程序为每个字符串打印
的标识哈希码new String
String#intern
输出结果如下:
366712642
366712642
366712642
true
1829164700
2018699554
2018699554
false
1311053135
1311053135
1311053135
true
118352462
1550089733
1550089733
false
可以看到,对于字符串"java"
,new String
的哈希码与字符串文字的哈希码不同,但后者是与调用String#intern
的结果相同的 - 这意味着String#intern
确实返回了与文字本身完全相同的字符串。
我还添加了字符串"true"
作为另一个测试用例。它显示了相同的行为,因为可以假设在引导VM之前已经出现了字符串true
。
答案 2 :(得分:1)
您没有正确使用intern
。 intern
不会修改它所调用的字符串对象(字符串无论如何都是不可变的),但返回该字符串的规范表示 - 您只是丢弃它。相反,您应该将其分配给变量并在检查中使用该变量。 E.g:
sj1 = sj1.intern();
答案 3 :(得分:1)
在OpenJDK 1.8.0u151和OpenJDK 9.0.4上
char[] cj = {'j','a','v','a'};
String sj = new String(cj);
sj.intern();
String sc = "java";
System.out.println(sj == sc);
打印true
。但是,此==
检查取决于String
在执行String sc = "java"
之前被截断到字符串池的内容。由于编译时间String
常量由Java编译器实现,sc
引用现在指向字符串池中的“java”,其中sj.intern()
使用s1
引用。
如果你尝试在之前分配String
“java”:
String before = "java"; // interned before by compiler
char[] cj = {'j','a','v','a'};
String sj = new String(cj);
sj.intern();
String sc = "java";
System.out.println(sj == sc);
代码现在将打印false
,因为sj.intern()
现在没有副作用,因为“java”String
之前已被实习。
要调试问题,请在到达失败检查之前检查内部字符串池中的内容。这可能取决于您的JVM供应商或版本。
有人会争辩说,仅仅为了将值添加到字符串池中而产生的副作用调用intern()
是没有意义的。写sj = sj.intern()
是实习String
。