JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现(重写覆盖了原来的),而不再是比较类在堆内存中的存放地址了。
equals方法,比较两个对象是否相等,比较的是地址(初始)和值(被重写后)
* public boolean equals(Object obj){
* return (this == obj);
* }
*
* Object中的equals方法是比较对象的地址值,没有什么意义,需要重写它。
* 因为在开发中我们通常比较的是对象中的属性值,我们认为相同属性是同一个对象,这样我们就需要重写equals方法。
==和equals方法的区别
共同点:都可以做比较,返回值都是boolean
区别:1,==是比较运算符号,既可以比较基本数据类型,也可以比较引用数据类型,基本数据类型比较的是值,引用数据类型比较的是地址值
2,equals方法只能比较引用数据类型,equals方法在没有重写之前,比较的是地址值,底层依赖的是==号,但是比较地址值是没有意义的,我们需要重写equals方法比较对象中的属性值。默认情况下也就是从超类Object继承而来的equals方法与‘==’是完全等价的,比较的都是对象的内存地址,但我们可以重写equals方法,使其按照我们的需求的方式进行比较,如String类重写了equals方法,使其比较的是字符的序列,而不再是内存地址。
在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof。因为使用instanceof判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。如果两者存在继承关系,肯定会返回 true 。所以用instanceof不太好。
equals()的重写规则:
- 自反性。对于任何非null的引用值x,x.equals(x)应返回true。
- 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。
- 传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。
static class B extends A{
@Override
public boolean equals(Object obj) {
if(obj instanceof B){
return true;
}
return super.equals(obj);
}
}
- 一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。
- 对于任何非空引用值x,x.equal(null)应返回false。
//import java.util.Objects;
public class Test {
/**
* @param args
* equals方法,比较两个对象是否相等
* public boolean equals(Object obj){
* return (this == obj);
* }
*
* Object中的equals方法是比较对象的地址值,没有什么意义,需要重写它。
* 因为在开发中我们通常比较的是对象中的属性值,我们认为相同属性是同一个对象,这样我们就需要重写equals方法。
==和equals方法的区别
共同点:都可以做比较,返回值都是boolean
区别:1,==是比较运算符号,既可以比较基本数据类型,也可以比较引用数据类型,基本数据类型比较的是值,引用数据类型比较的是地址值
2,equals方法只能比较引用数据类型,equals方法在没有重写之前,比较的是地址值,底层依赖的是==号,但是比较地址值是没
有意义的,我们需要重写equals方法比较对象中的属性值。
*/
public static void main(String[] args) {
Student s1 = new Student("a", 1);
Student s2 = new Student("b", 2);
boolean b = s1.equals(s2); //比较2个对象是否相等
System.out.println(s1 == s2);
System.out.println(b); //重写之后比较的是对象中的属性值
}
}
class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/*@Override
public String toString() {
return "姓名是:" + name + ",年龄是:"+ age;
}*/
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
//重写equals方法
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == this.getClass()) { //if (obj instanceof Student) {
Student s = (Student)obj;
if (s.getName() == null || this.name == null) {
return false;
}
else
return this.name.equals(s.name) &&this.age == s.age;
}
return false;
}
@Override
public int hashCode() {
return name.hashCode()+new Integer(age).hashCode();
//return Objects.hash(name,age);
}
}
输出:
false
false
下面是 String 的 equals() 方法:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
为什么重写equals()的同时还得重写hashCode():
这个问题主要是针对映射相关的操作(Map等接口)。学过数据结构的同学都知道Map接口的类会使用到键对象的哈希码,当我们调用put方法或者get方法对Map容器进行操作时,都是根据键对象的哈希码来计算存储位置的,因此如果我们对哈希码的获取没有相关保证,就可能会得不到预期的结果。在java中,我们可以使用hashCode()来获取对象的哈希码,其值就是对象的存储地址,这个方法在Object类中声明,因此所有的子类都含有该方法。hashCode的意思就是散列码,也就是哈希码,是由对象导出的一个整型值,散列码是没有规律的,如果x与y是两个不同的对象,那么x.hashCode()与y.hashCode()基本是不会相同的.
编写一个完美的重写equals()方法的几点建议
下面给出编写一个完美的equals方法的建议(出自Java核心技术 第一卷:基础知识):
1)显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量(参数名命名,强制转换请参考建议5)
2)检测this与otherObject是否引用同一个对象 :if(this == otherObject) return true;(存储地址相同,肯定是同个对象,直接返回true)
3) 检测otherObject是否为null ,如果为null,返回false.if(otherObject == null) return false;
4) 比较this与otherObject是否属于同一个类(视需求而选择)
- 如果equals的语义在每个子类中有所改变,就使用getClass检测 :if(getClass()!=otherObject.getClass()) return false;
- 如果所有的子类都拥有统一的语义,就使用instanceof检测 :if(!(otherObject instanceof ClassName)) return false;
5) 将otherObject转换为相应的类类型变量:ClassName other = (ClassName) otherObject;
6) 现在开始对所有需要比较的域进行比较 。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则就返回flase。
- 如果在子类中重新定义equals,就要在其中包含调用super.equals(other)
- 当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明 相等对象必须具有相等的哈希码 。