使用字符串的==运算符

下面的代码不应该打印“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"); } } } 

这种行为是因为实习String#intern的文档中描述了这种行为(包括为什么它出现在你的代码中,即使你从不调用String#intern ):

最初为空的字符串池由String类私有维护。

调用intern方法时,如果池已经包含等于此String对象的字符串equals(Object)equals(Object)方法确定equals(Object) ,则返回池中的字符串。 否则,将此String对象添加到池中,并返回对此String对象的引用。

因此,对于任何两个字符串st ,当且仅当s.equals(t)为真时, s.intern() == t.intern()才为真。

所有文字字符串和字符串值常量表达式都是实体。 字符串文字在Java语言规范的 §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"; } } 

输出:

  [静态] s2 == s1? 真正
 [实习前] s3 == s1? 假
 [实习后] s3 == s1? 真正 

走过那个:

  1. 分配给s1的文字会自动实现,因此s1最终会引用池中的字符串。
  2. 分配给s2的文字也是自动实习的,因此s2最终指向s1指向的相同实例。 这很好,即使代码的两位代码可能彼此完全不知道,因为Java的String实例是不可变的 。 你无法改变它们。 您可以使用类似toLowerCase方法来获取带有更改的字符串,但是调用toLowerCase (等)的原始字符串保持不变。 因此,可以安全地在不相关的代码之间共享它们。
  3. 我们通过运行时操作创建一个新的 String实例。 即使新实例与实习者具有相同的字符序列,它也是一个单独的实例。 运行时不会自动生成动态创建的字符串,因为涉及成本:在池中查找字符串的工作。 (编译时,编译器可以将成本转嫁给自己。)所以现在我们有两个实例,一个是s1s2指向的,一个是s3指向的。 所以代码显示s3 != s1
  4. 然后我们明确实习s3 。 也许这是我们计划长期坚持的大字符串,我们认为它可能会在其他地方重复。 因此,我们接受实习的工作,以换取潜在的内存节省。 由于按定义实习意味着我们可能会返回一个新的引用,我们将结果分配回s3
  5. 我们可以看到,确实, s3现在指向同一个实例s1s2指向。

硬编码字符串被编译到JVM的字符串表中,字符串表包含唯一的字符串 – 即编译器只存储一个“Hi”副本,因此您要比较同一个对象 ,因此==有效。

如果你实际使用构造函数创建一个新的String,比如new String("Hi") ,你得到一个不同的对象。

java中有一个String缓存。 在这种情况下,从具有相同引用的高速缓存返回相同的对象。

主要原因是因为从String Pool拾取了"Hi" 。 不可变对象必须具有某种缓存,以便它可以更好地执行。 所以String类是不可变的,它使用String Pool进行基本缓存。

在这种情况下, "Hi"位于String池中,所有值为"Hi"的String将具有相同的String Pool引用。