Java程序员都知道java.lang.Object类,这是所有类的超类。Object类中提供了几个public的方法,比如:
public boolean equals(Object var1) {
return this == var1;
}
public String toString() {
return this.getClass().getName() + “@” + Integer.toHexString(this.hashCode());
}
这些public方法第一是提供给所有的子类去扩展(覆盖),第二是明确了Java中的类所具备的通用约定。因此Java中的类在覆盖这些方法是,都需要遵守通用约定,避免程序员们各玩各的。
那具体应该怎么覆盖,又应该遵守那些通用约定呢? 其实这是一个非常复杂的问题,正如Java大师约书亚·布洛克(Joshua Bloch)所说:它看似简单,但是往往很多高级程序员也无法完全正确的实现,并且如果不严格遵守,往往会导致非常严重的后果。
2、正文
2.1 什么时候需要重写equals方法
总结一句话就是:当我们需要比较两个对象是否“逻辑相等”时,可能需要考虑重写equals方法,比如我们需要比较值类型的类Integer、String,这些类经常需要用于承载和比较值是否相等,或者用于做个Map、Set等集合的Key值,在这些场景下我们是需要严格的去重写equals方法的。(我这里说的是可能,是因为很多情况下无招胜有招,我们或许不需要重写equals方法,至于那些场景不需要重写equals方法这个会在后面说!)
2.2 什么时候不需要重写equals方法
不需要实现equals方法的场景非常多,我们大致的举例说明一下:
-
类的每个实例唯一。比如说:枚举类型,枚举类型虽然也属于上面说的“值类”,但是由于枚举类的每个值只会存在一个对象,因此不需要重写equals方法
-
类的访问权限是私有的(类私有、包级私有),并且确保equals方法不会被调用。说白了就是其他类无法调用到这个类的equals方法
-
超类覆写的equals方法,在子类仍然适用。这种情况下我们就无需再多此一举了,在Java的JDK源码中,set、List、Map都直接使用了超类的equals方法,比如在HashSet提供的方法中,并未有equals方法的实现,这是因为其父类AbstractSet中覆写了equals方法,且父类实现的逻辑对于子类也是可用的。
- 类本身无需提供“逻辑相等”的功能。这种情况其实非常常见,比如我们在实际开发中经常写的工具类,这些类的实例只是用来完成某些任务,并不需要比较它们是否逻辑相等。比如Java提供的java.util.regex.Pattern类,并未实现equals方法,因为它觉得没人会比较两个Pattern对象是否相等。
2.3 重写equals方法需要遵守哪些规则
重写equals方法有几条看起来很简单,但是实现起来几乎无法完全保证的约定:
-
自反性(Reflexivity):非null情况下,x.equals(x)必须为true
-
对称性(Symmetry):非null情况下,x.equals(y) = true则y.equals(x) = true
-
传递性(Transitive):非null情况下,x.equals(y) = true && y.equals(z) = true则x.equals(z) = true
-
一致性(Consistent):非null情况下,x.equals(y) = true只要x或y其中任意一个对象不被修改,那么x.equals(y) = true应该恒成立
-
非空性(Non-nullity):x不为null的情况下,x.equals(null)必须返回false
看到这五条规则是不是觉得头大,平时我们在写的时候,压根就没考虑过这么多条条框框,只有能实现功能上的逻辑相等了就行!
其实我觉得这么想也不能说是完全不对,因为如果一定要完完全全的按照它这个规范来,那么面向对象很多功能都用不了了,比如说继承。 其实Java的JDK中也是有些代码不满足上面说的这五条规范的,比如我们看下如下这段代码(猜猜它会输出什么?):
package com.lizba.tips;
import java.sql.Timestamp;
import java.util.Date;
/**
*
* Java自带jdk equals方法的对称性测试
*
* @Author: Liziba
* @Date: 2021/10/24 14:48
*/
public class EqualsDemo {
public static void main(String[] args) {
Date date = new Date();
Timestamp timestamp = new Timestamp(date.getTime());
System.out.println("Date equals to Timestamp: " + date.equals(timestamp));
System.out.println("Timestamp equals to Date: " + timestamp.equals(date));
}
}
是的你没看错,第一个输出了true,第二个输出了false。很显然这不满足第二点:对称性(Symmetry)。
Date equals to Timestamp: true
Timestamp equals to Date: false
基于这种情况,Java并没有很好的办法去解决。只能说告诉你不用混用Date和Timestamp,并且无论如何不要去equals比较Date和Timestamp,这个在Timestamp中的类和equals方法上也是有说明的!
2.4 实现高质量equals方法的诀窍
上面聊了一些什么时候需要重写equals方法、什么时候不需要重写equals方法、重写equals方法需要遵守的规则。这里我们聊一聊实现高质量equals方法的诀窍。 在这里我将会引用java.util.AbstractSet类中的equals方法来阐述如何写一个高质量的equals方法,因为小捌发现它非常经典。
java.util.AbstractSet中的equals方法:
public abstract class AbstractSet extends AbstractCollection implements Set {
// …
public boolean equals(Object o) {
if (o == this)
题外话
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
这里我为大家准备了一些我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~
欢迎评论区讨论。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~
[外链图片转存中…(img-iM0j8y1e-1715774436312)]
欢迎评论区讨论。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!