如果在Java中“==”用于比较引用,为什么这些字符串会被评估为true?

14

正如所述,== 运算符比较对象引用以检查它们是否引用堆上的同一对象。如果是这样,为什么我会得到这段代码的 "Equal" 结果呢?

public class Salmon {
    public static void main(String[] args) {

        String str1 = "Str1";
        String str2 = "Str1";

        if (str1 == str2) {
            System.out.println("Equal");
        } else {
            System.out.println("Not equal");
        }
    }
}

1
你确定你使用的是 == 还是 = 吗? - jmj
修改了一个代码示例,抱歉。 - Eugene
1
请确保指明语言为JAVA。其他语言将有其他实现方式。无论.NET中的==运算符被重载为执行什么操作。 - CodingBarfield
== 检查对象是否相等时,检查引用是否相同。有关如何创建字符串对象的详细信息,请参见http://stackoverflow.com/questions/15761283/where-does-the-string-object-constructed-using-new-resides-apart-from-heap/15761957#15761957。 - Nirbhay Mishra
5个回答

39
程序将会打印出Equal。(至少在使用Sun Hotspot和suns Javac时)这里是在http://ideone.com/8UrRrk演示的。

这是因为字符串字面常量被存储在一个字符串池中,而字符串引用可能会被重用。

进一步阅读:


然而这个:

public class Salmon {
    public static void main(String[] args) {

        String str1 = "Str1";
        String str2 = new String("Str1");

        if (str1 == str2) {
            System.out.println("Equal");
        } else {
            System.out.println("Not equal");
        }
    }
}

会打印出Not equal,因为new保证会引入一个新的引用。

所以,经验法则是:始终使用equals方法来比较字符串。


我很久以前就想要一个像这样的Web应用程序。尽管曾考虑过自己编写,但现在已经有了。+1 链接到 ideone.com。 - Alex Jasmin
3
为了完整性,new String("Str1").intern()也会输出"Equal" :) - OscarRyz

3

这段代码不会打印出Equal
但如果两个字符串相同,这种情况就很特殊。

现在您已经更新了代码,它的输出是:

一个简单(但不完全准确)的解释是编译器看到两个字符串相同并执行如下操作:

String str1 = "Str1";
String str2 = str1;

这里实际发生的是编译器会把字面字符串放在“字符串常量池”中。

由于String是不可修改的(即它是不可变的),字符串的文本内容(在编译期间找到的)被放置在一个“池子”中。
这样,如果两个不同的字符串具有相同的内容(就像在这个特定的案例中一样),内存不会浪费来存储两次“Str1”。


3

Java 在运行时会将所有字符串存储在内部的字符串表中。由于在内存中它们存储在同一位置,所以这两个字符串的引用是相同的。因此,Equal

您的说法是正确的,== 比较的是对象引用。但如果使用除了字符串之外的其他类尝试同样的操作,则不会得到相同的结果。


这是真的吗?你能给我一个参考来支持这个说法吗? - Richard J. Ross III
1
aioobe 给出了更好的解释,因为这种行为不能保证。而且它仅适用于字符串字面量,而不是程序中的每个字符串。 - Joey
2
从Java API文档中可以看到:“所有字面字符串和字符串常量表达式都会被池化。” 请参见http://download.oracle.com/javase/1.4.2/docs/api/java/lang/String.html#intern%28%29 注意,只有常量字符串才会自动池化。您可以通过调用String.intern()来池化动态创建的字符串。 - Hannes de Jager

2

大家可能忘记了,将字面字符串放入池中的过程称为“interning”。String类有一个名为intern()的方法。该方法将任何字符串放入池中,即使它最初不在池中(不是字面)。这意味着像下面这样的代码:

String a = "hello";
String b = new String("hello");
b = b.intern();

System.out.println(a == b);

将打印“true”。那么,为什么有人需要这个呢?可以想象,如果字符串长度相同但接近末尾不同,则字符串比较a.equals(b)可能需要很长时间。(只需查看.equals()源代码即可。)
然而,直接比较引用与比较整数(在C中称为指针)相同,几乎是瞬间完成的。
那么,这给你什么?速度。如果您必须多次比较相同的字符串,您的程序性能将获得巨大的好处,如果您内部化这些字符串。但是,如果您只比较字符串一次,那么就不会有性能提升,因为内部化过程本身使用equals()。
希望这样解释清楚了。谢谢。

0

前面的评论已经很好地总结了。

我手头没有Java环境,但尝试以下操作应该能为您澄清问题(希望这能按照我的预期工作)。

String str1 = "Str1";  
String str2 = "Str"; str2 += "1";

现在应该打印出 Not equal


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接