使用'=='比较字符串?(Java)

3
String str1 = new String("I love programming");
String str2 = new String("I love programming");
boolean boo = str1 == str2; // evaluates to false

String str1 = "I love programming";
String str2 = "I love programming";
boolean boo = str1 == str2; // evaluates to true

为什么第一个求值为假,而第二个求值为真?

在这里你可以找到更多信息:Java字符串池是什么,"s"和new String("s")有何不同?


编译器可能会优化掉第二个字符串并将它们放入同一个对象中。由于字符串是不可变的,所以这样做没有任何问题。前两个字符串显式调用了它们的构造函数,因此创建了新的对象,很可能它们的内部指针也指向最后两个字符串的相同位置。 - lared
4个回答

13

==会在对象本身具有相同地址时返回true。为了节省空间和提高效率,重复的字面量会被优化为使用相同的地址。第二个 str1str2 相等,因此 == 返回true。

在第一个示例中,由于您是使用new关键字显式声明内存,所以str1str2没有相同的地址。因此,str1==str2评估为false

在测试相等性时,请使用 String.equals(); 函数。因此,str1.equals(str2); //true


@Tyler:这是否意味着Java在声明新字符串时实际上会在其内存中搜索相等的字符串?因此,在声明str2时,它发现str1与其相等,因此将其设置为相同的地址? - dokaspar
@dokaspar请查阅String.intern();函数的文档。我不完全确定每个JDK如何实现字符串驻留,但应该是类似这样的。请记住,只有字面量才会被驻留,而不是新对象。 - umop aplsdn
@dokaspar 我认为字符串的"相等"取决于不同的实现方式,但在这种情况下,编译器完成了它:它看到该字符串四次(两次在构造函数调用中),并将它们存储一次在类文件的常量池中。因此,在运行时没有查找,类文件引用多次相同的字符串(常量,而非对象)。读者的练习:不同类文件中的相等字符串字面值是否相同? - Silly Freak
好的,我测试过了...至少在OpenJDK 7上,它们似乎是相同的。我会说类加载器将类文件的常量池与已经interned的字符串进行匹配。 - Silly Freak

1
equals()方法比较字符串的内容,而==在Java中比较引用。

2
是的,但你没有回答问题。他没有使用比较方法。 - Miki
我只是指出equals和==之间的区别,因为他的问题很令人困惑。 - Coding Enthusiast
感谢您的努力。 - Miki

1

这个问题涉及到Java内存模型。

第一个比较语句返回false,因为你正在比较两个不同对象的不同引用。使用关键字new会在堆中分配内存空间到两个不同的内存地址。第二个比较语句,JVM会将内存空间一次性“分配到栈”(从Java 7开始它们也在堆中)。编译器通过使两个变量指向相同的内存空间来优化内存使用情况,这就解释了等式结果是true的原因。

关于堆、栈等方面的有趣文章,请参阅JVM Internals Blog

干杯!


这个答案不太正确:Java语言规范要求字符串字面量必须被放入常量池中,这意味着它们不能在堆栈上分配。 - meriton
你的评论是正确的,从Java 7开始,谢谢。 - Abderrazak BOUADMA
实际上,自Java 1.0以来就一直是这样的,例如请参见http://titanium.cs.berkeley.edu/doc/java-langspec-1.0/3.doc.html#101083。 - meriton

0
不像C语言。Java中的(==)比较字符串变量的引用。它比较存储字符串的两个地址。如果要按值比较它们,您需要使用string1.equals(string2)。

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