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");
,那么 x
和 y
指的是两个不同的对象。 x == y
在这种情况下将为假。 Object.java
提供的 equals
方法的默认实现考虑 ==
,因为它无法决定子类的逻辑相等参数。由于 x
和 y
引用的两个对象的逻辑值相同,因此 equals
被覆盖,并且 return true
对于这种情况也是如此。
在学习的过程中我们经常会遇到很多这样的问题,我们最好的导师是**java文档,jdk源代码(是的java代码是开放的你可以阅读它),自己尝试(为此你可以尝试通过不同的方式创建字符串对象,然后尝试调用 .equals
),调试
我推荐 作为简单且无可争议的 证明。
这里还有两个证据 String
覆盖 Object.equals(Object)
javadoc就是这么暗示的。请注意,相等性基于字符串的 contents。事实上,即使 String.equals(Object)
方法存在不同的 javadoc 也意味着该方法已被重载......考虑到生成 javadoc 的方式。
source code是这么说的。显然,链接代码 是 重载。很明显,它不仅仅是比较对象引用。
你的推理有问题。您假设具有相同字符序列的任意两个字符串将由同一个对象表示。这在一般情况下是不正确的。 只有情况总是如此,如果这两个字符串是从源代码中的字符串文字获得的。
只有字符串文字会自动驻留。其他字符串仅在您通过调用 String.intern()
.
生成它们时才会被保留
问这个问题的原因是 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");
,那么 x
和 y
指的是两个不同的对象。 x == y
在这种情况下将为假。 Object.java
提供的 equals
方法的默认实现考虑 ==
,因为它无法决定子类的逻辑相等参数。由于 x
和 y
引用的两个对象的逻辑值相同,因此 equals
被覆盖,并且 return true
对于这种情况也是如此。
在学习的过程中我们经常会遇到很多这样的问题,我们最好的导师是**java文档,jdk源代码(是的java代码是开放的你可以阅读它),自己尝试(为此你可以尝试通过不同的方式创建字符串对象,然后尝试调用 .equals
),调试
我推荐
这里还有两个证据 String
覆盖 Object.equals(Object)
javadoc就是这么暗示的。请注意,相等性基于字符串的 contents。事实上,即使
String.equals(Object)
方法存在不同的 javadoc 也意味着该方法已被重载......考虑到生成 javadoc 的方式。source code是这么说的。显然,链接代码 是 重载。很明显,它不仅仅是比较对象引用。
你的推理有问题。您假设具有相同字符序列的任意两个字符串将由同一个对象表示。这在一般情况下是不正确的。 只有情况总是如此,如果这两个字符串是从源代码中的字符串文字获得的。
只有字符串文字会自动驻留。其他字符串仅在您通过调用 String.intern()
.