下面的代码不应该打印“Bye”,因为==运算符用于比较引用,但奇怪的是,仍然会打印“Bye”。为什么会这样?我正在使用Netbeans 6.9.1作为IDE。
public class Test {
public static void main(String [] args) {
String test ="Hi";
if(test=="Hi"){
System.out.println("Bye");
}
}
}
答案 0 :(得分:42)
此行为是因为实习。 String#intern
的文档中描述了这种行为(包括为什么它会显示在您的代码中,即使您从未调用String#intern
):
字符串池(最初为空)由类
String
私有维护。当调用
intern
方法时,如果池已经包含等于String
方法确定的equals(Object)
对象的字符串,则返回池中的字符串。否则,将此String
对象添加到池中,并返回对此String
对象的引用。对于任何两个字符串
s
和t
,当{且仅s.intern() == t.intern()
为真时,true
为s.equals(t)
。所有文字字符串和字符串值常量表达式都是实体。字符串文字在Java Language Specification的§3.10.5中定义。
例如:
public class Test {
private String s1 = "Hi";
public static void main(String [] args) {
new Test().test();
System.exit(0);
}
public void test() {
String s2 ="Hi";
String s3;
System.out.println("[statics] s2 == s1? " + (s2 == s1));
s3 = "H" + part2();
System.out.println("[before interning] s3 == s1? " + (s3 == s1));
s3 = s3.intern();
System.out.println("[after interning] s3 == s1? " + (s3 == s1));
System.exit(0);
}
protected String part2() {
return "i";
}
}
输出:
[statics] s2 == s1? true [before interning] s3 == s1? false [after interning] s3 == s1? true
走过那条:
s1
的字面值会自动实现,因此s1
最终会引用池中的字符串。s2
的文字也是自动实习的,因此s2
最终指向同一个实例s1
。这很好,即使代码的两位代码可能彼此完全不知道,因为Java的String
实例是不可变的。你无法改变它们。您可以使用toLowerCase
之类的方法来获取带有更改的 new 字符串,但您调用toLowerCase
(等)的原始字符串保持不变。因此,他们可以安全地在不相关的代码中共享。String
实例。即使新实例与实际实例具有相同的字符序列,它也是单独的实例。运行时不会自动动态创建字符串,因为涉及成本:在池中查找字符串的工作。 (编译时,编译器可以将该成本转嫁给自己。)现在我们有两个实例,一个s1
和s2
指向,一个s3
指向。因此代码显示s3 != s1
。s3
。也许这是我们计划长期坚持的大字符串,我们认为它可能会在其他地方重复出现。因此,我们接受实习的工作,以换取潜在的内存节省。由于按定义进行实习意味着我们可能会返回新的参考,我们会将结果分配回s3
。s3
现在确实指向同一个实例s1
和s2
指向。答案 1 :(得分:10)
硬编码字符串被编译到JVM的String Table中,该表包含唯一的字符串 - 即编译器只存储一个“Hi”副本,因此您要比较同一个对象,所以{ {1}}有效。
如果您实际使用构造函数创建新String,例如新==
,则将获取另一个对象。
答案 2 :(得分:1)
java中有一个String缓存。在这种情况下,从具有相同引用的缓存返回相同的对象。
答案 3 :(得分:0)
主要原因是"Hi"
从String Pool
中获取。不可变对象必须具有某种缓存,以便它可以更好地执行。所以String
类是不可变的,它使用String Pool
进行基本缓存。
在这种情况下,"Hi"
位于字符串池中,所有值为"Hi"
的字符串对字符串池都具有相同的引用。