String class 中需要重写 equals 方法有什么用?

What is the need to override equals method in String class?

问这个问题的原因是 Java 中的 String 被 interned 所以

String s1 = "Hello";

String s2 = "Hello";

s1和s2都指向内存中的同一个对象。不会创建两个不同的对象。

s1.equals(s2) 应该 return 正确,确实如此。 现在在 java 文档中它说字符串 class 覆盖了对象 class 的 equals 方法。但真的如此吗?

因为即使字符串 class 没有覆盖 Object.equals(),equals 方法的输出也会保持不变。

如果它确实覆盖了,为什么需要它?

String 覆盖 equals。简单测试,

String a = "a";
String b = new String("a");
System.out.println(a == b); // <-- false
System.out.println(a.equals(b)); // <-- true

QED.

并非全部"String in Java are interned"。尝试从文件或控制台读取内容,这些字符串不是 "interned",因此需要覆盖 equals()(以及 hashcode())。

String 确实覆盖了 equals(),如下面的源代码摘录所示。 Click here for source code.

然而,这并不意味着它一直使用这个等于代码。正如您可能知道的那样,Java 首先将代码从字节码解释为机器码,并且仅在一段时间后 JIT 将其编译为机器码。现在,在解释阶段,java.lang.String#equals 方法 用于比较字符串。但是,它非常慢。这就是为什么当 JIT 编译代码时,编译后的代码使用不同版本的 equals(),速度要快得多(我假设不是用 Java 编写的)。不幸的是,不能给你任何细节,所以你必须自己深入研究。

回到你的问题,为什么要覆盖 equals()?

  • 您可以将字符串初始化为文字,如您提供的代码 (String s = "..")。在这种情况下,所有字符串从一开始就被保留。引自 javadoc:
  • All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java Language Specification

所以你可以通过引用比较它们,使用==。它会工作得很好!但是想象一下你像对象一样初始化字符串:

String s1 = new String("some string");
String s2 = new String("some string");

如果用==比较这两个字符串,那么结果就是false。那是因为 默认情况下不驻留对象字符串 。您必须通过对它们使用 .intern() 手动执行此操作,然后通过引用比较它们。或者,如果您不想这样做,那就是 equals() 出现的地方。如果您对对象字符串(不是 interned)执行 s1.equals(s2),则结果为真。

  • 但是,驻留字符串有点邪恶。 字符串池大小有限,它不能再增长了。当你 运行 你的 main() 方法时,它已经有一堆字符串了。所以它被系统使用。现在,如果你实习遇到的每一个字符串,然后通过引用(“==”)比较它们,你能想象会发生什么吗? :) 字符串池只是 space 中的 运行,这很糟糕。

如果您想了解更多 java.lang.String,我强烈建议您观看 Alexey Shipilev。 The link to the video presentation。他几乎证实了我上面写的一切。

如果您有 String x = new String("a")String y = new String("a");,那么 xy 指的是两个不同的对象。 x == y 在这种情况下将为假。 Object.java 提供的 equals 方法的默认实现考虑 ==,因为它无法决定子类的逻辑相等参数。由于 xy 引用的两个对象的逻辑值相同,因此 equals 被覆盖,并且 return true 对于这种情况也是如此。

在学习的过程中我们经常会遇到很多这样的问题,我们最好的导师是**java文档,jdk源代码(是的java代码是开放的你可以阅读它),自己尝试(为此你可以尝试通过不同的方式创建字符串对象,然后尝试调用 .equals),调试

我推荐 作为简单且无可争议的 证明

这里还有两个证据 String 覆盖 Object.equals(Object)

  1. javadoc就是这么暗示的。请注意,相等性基于字符串的 contents。事实上,即使 String.equals(Object) 方法存在不同的 javadoc 也意味着该方法已被重载......考虑到生成 javadoc 的方式。

  2. source code是这么说的。显然,链接代码 重载。很明显,它不仅仅是比较对象引用。


你的推理有问题。您假设具有相同字符序列的任意两个字符串将由同一个对象表示。这在一般情况下是不正确的。 只有情况总是如此,如果这两个字符串是从源代码中的字符串文字获得的

只有字符串文字会自动驻留。其他字符串仅在您通过调用 String.intern().

生成它们时才会被保留