珞冬聊Java_什么时候需要我们重写equals()方法?

本文将为你解决以下几个核心问题

  1. == 和equals方法的区别从本质上去分析到底在哪里?(见CH2)
  2. 为什么总会出现要求equals方法重写,甚至在List中要求必须进行equals方法的重写?(见CH3)

目录

  • 1 源起
  • 2 擒贼先擒王——看看底层API
  • 3 equals()与‘==’的区别
  • 4 equals()方法该如何重写?
    • 4.1 无属性的类重写equals()方法
    • 4.2 含属性的类重写equals()方法
    • 4.3 含继承关系的子父类的equals方法

1 源起

在学习java过程中,很多初学者会被 == 运算 和.equals() 方法所迷惑,等到好不容易在String部分弄懂了两者的关系后,但在对象的对比和List中关于需要重写类的.equals()方法时又被搞晕。
今天一个初学者又再次问了阿冬哥这个问题,自己通过参考网络的文献等资料,这次尝试以最清楚简明的思路给大家彻底理清这块的思路。
珞冬聊Java_什么时候需要我们重写equals()方法?_第1张图片

2 擒贼先擒王——看看底层API

说起equals方法,我们都知道是超类Object中的一个基本方法,用于检测一个对象是否与另外一个对象相等。而在Object类中这个方法实际上是判断两个对象是否具有相同的引用,如果有,它们就一定相等。其源码如下:

	public boolean equals(Object obj) { 
	  return (this == obj);    
    }

实际上我们知道所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说 Object 的 equals() 方法是比较两个对象的内存地址是否相等,即若 object1.equals(object2) 为 true,则表示 equals1 和 equals2 实际上是引用同一个对象。

  • 即我们在String时用到的equals方法时,遇到的按照value去对比的情况,其实也只是对象的属性在方法区时,对于相同的内容,比如
class A{
String str1 = “ABC”;
String str2 = “ABC”;
}

这里的在方法区只有一块内存存储字符串"ABC",然后str1和str2同时指向那块内存的地址,所以,String的equals方法其本质还是.==去对比内存的地址,这也是本质,是核心,理解这句话,就是本文的目的之一所在。

3 equals()与‘==’的区别

或许这是我们面试时更容易碰到的问题”equals方法与‘= = ’ 运算符有什么区别?“,并且常常我们都会胸有成竹地回答:“equals比较的是对象的内容,而‘= =’比较的是对象的地址。”。但是从前面我们可以知道equals方法在Object中的实现也是间接使用了‘==’运算符进行比较的,所以从严格意义上来说,我们前面的回答并不完全正确。我们先来看一段代码并运行再来讨论这个问题。

package com.zejian.test;
public class Car {
	private int batch;
	public Car(int batch) {
		this.batch = batch;
	}
	public static void main(String[] args) {
		Car c1 = new Car(1);
		Car c2 = new Car(1);
		System.out.println(c1.equals(c2));
		System.out.println(c1 == c2);
	}
}

运行结果

false
false

分析:对于‘= =’运算符比较两个Car对象,返回了false,这点我们很容易明白,毕竟它们比较的是内存地址,而c1与c2是两个不同的对象,所以c1与c2的内存地址自然也不一样。现在的问题是,我们希望生产的两辆的批次(batch)相同的情况下就认为这两辆车相等【即以批次判断是否是相同的车】,但是运行的结果是尽管c1与c2的批次相同,但equals的结果却反回了false。当然对于equals返回了false,我们也是心知肚明的,因为equal来自Object超类,访问修饰符为public,而我们并没有重写equal方法,故调用的必然是Object超类的原始方equals方法,根据前面分析我们也知道该原始equal方法内部实现使用的是’=='运算符,所以返回了false。因此为了达到我们的期望值,我们必须重写Car的equal方法,让其比较的是对象的批次(即对象的内容),而不是比较内存地址,于是修改如下:

@Override
	public boolean equals(Object obj) {
		if (obj instanceof Car) { //这里只是判断是不是car类型,但此时obj作为Object类型引入,编译器还是仅限于知道它是一个Object对象,而不是Car类型的对象
			Car c = (Car) obj;   //在这里,因为传进来的时obj对象,所以你需要先if判断,然后强转,不然你下面无法获取batch属性的(Object可没有batch属性,所以无法调出),另外你重写equals方法的目的就是为了对比值得等量逻辑关系
			return batch == c.batch;
		}
		return false;
	}

使用instanceof来判断引用obj所指向的对象的类型,如果obj是Car类对象,就可以将其强制转为Car对象,然后比较两辆Car的批次,相等返回true,否则返回false。当然如果obj不是 Car对象,自然也得返回false。我们再次运行:

true
false

嗯,达到我们预期的结果了。因为前面的面试题我们应该这样回答更佳

总结:默认情况下也就是从超类Object继承而来的equals方法与‘==’是完全等价的,比较的都是对象的内存地址,但我们可以重写equals方法,使其按照我们的需求的方式进行比较,如String类重写了equals方法,使其比较的是字符的序列,而不再是内存地址。

4 equals()方法该如何重写?

4.1 无属性的类重写equals()方法

  • 直接判断类型就可以
@Override
	public boolean equals(Object obj) {
		if (obj instanceof BigCar) {
			return true;
		}
		return false;
	}

4.2 含属性的类重写equals()方法

-先判断类型,在强制转换判断属性

@Override
	public boolean equals(Object obj) {
		if (obj instanceof Car) { //这里只是判断是不是car类型,但此时obj作为Object类型引入,编译器还是仅限于知道它是一个Object对象,而不是Car类型的对象
			Car c = (Car) obj;   //在这里,因为传进来的时obj对象,所以你需要先if判断,然后强转,不然你下面无法获取batch属性的(Object可没有batch属性,所以无法调出),另外你重写equals方法的目的就是为了对比值得等量逻辑关系
			return batch == c.batch;
		}
		return false;
	}

4.3 含继承关系的子父类的equals方法

你可能感兴趣的:(java)