为什么 intern() 不适用于文字 'java'?
Why intern() does not work with literal 'java'?
我试过下面的代码:
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"
吗?为什么当字面量为 "java"
时,上述参考比较的计算结果为 true
、except?
您没有正确使用 intern
。 intern
不会修改它所调用的字符串对象(无论如何字符串都是不可变的),但是 returns 是该字符串的规范表示 - 您只是丢弃了它。相反,您应该将其分配给一个变量并在检查中使用该变量。例如:
sj1 = sj1.intern();
当 JVM 第一次遇到 new String(new char[] {'a', 'b', 'h', 'i'})
字符串并且您对其调用 intern()
时,您刚刚创建的引用成为规范引用并存储在字符串常量池中。然后 "abhi"
从常量池中拉出 - 你的规范实例已被重用。
您的问题是在您的程序开始之前,文字 "java"
存在于常量字符串池中 - JVM 只是将它放在那里以供某些使用。因此,在 new String(new char[] {'j', 'a', 'v', 'a'})
上调用 intern()
不会 而不是 实习你的参考。相反,它 return 是常量池中的 pre-existing 规范值,您可以愉快地忽略 return 值。
您不应忽略 return 值,而应使用它。你永远不知道自从 JVM 启动以来,你的 "definitely original" 字符串是否一直存在于常量池中。无论如何,所有这些都依赖于实现,您应该始终使用由 intern()
方法编辑的 return 引用,或者从不使用。请勿混用。
在 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 sc = "java"
之前,哪些 String
已被驻留在字符串池中。由于编译时 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
.
的正确方法
几乎可以肯定是正确的(+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"
作为另一个测试用例。它显示了相同的行为,因为可以假设字符串 true
在引导 VM 之前已经出现。
我试过下面的代码:
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"
吗?为什么当字面量为 "java"
时,上述参考比较的计算结果为 true
、except?
您没有正确使用 intern
。 intern
不会修改它所调用的字符串对象(无论如何字符串都是不可变的),但是 returns 是该字符串的规范表示 - 您只是丢弃了它。相反,您应该将其分配给一个变量并在检查中使用该变量。例如:
sj1 = sj1.intern();
当 JVM 第一次遇到 new String(new char[] {'a', 'b', 'h', 'i'})
字符串并且您对其调用 intern()
时,您刚刚创建的引用成为规范引用并存储在字符串常量池中。然后 "abhi"
从常量池中拉出 - 你的规范实例已被重用。
您的问题是在您的程序开始之前,文字 "java"
存在于常量字符串池中 - JVM 只是将它放在那里以供某些使用。因此,在 new String(new char[] {'j', 'a', 'v', 'a'})
上调用 intern()
不会 而不是 实习你的参考。相反,它 return 是常量池中的 pre-existing 规范值,您可以愉快地忽略 return 值。
您不应忽略 return 值,而应使用它。你永远不知道自从 JVM 启动以来,你的 "definitely original" 字符串是否一直存在于常量池中。无论如何,所有这些都依赖于实现,您应该始终使用由 intern()
方法编辑的 return 引用,或者从不使用。请勿混用。
在 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 sc = "java"
之前,哪些 String
已被驻留在字符串池中。由于编译时 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
.
真的证明它很难,因为大部分字符串池都驻留在 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"
作为另一个测试用例。它显示了相同的行为,因为可以假设字符串 true
在引导 VM 之前已经出现。