JAVA中Object是所有类的基类,所以任何一个JAVA类都继承了Object中的equals方法。Object类中equals方法原型如下:

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

通过equals方法的实现可以看到,它与 == 的作用是相同的,而==号比较的是两对象地址是否相同,所以equals方法默认比较的也是两个对象的地址。如果两对象地址相同则返回true,否则返回false。

在我们使用String等内置的数据类型时,equals方法好像并没有什么异样,他就是比较两个对象内容是否相同,我们用起来也非常的方便。这是因为Java在String等类的内部已经对equals方法进行了重写,所以我们使用的时候就是比较内容,即便两个对象地址不同,他也会返回true。

但是当我们对自己封装的类对象使用equals方法时,直接使用就会发生意想不到的结果。

一个简单的测试用例如上图所示,p1与p1相同,p1与p2不同,这是符合我们直觉的。(但这里的用法并不正确,后续会解释)

我们再来看另一个例子:

Person p1=new Person("张三",18);
Person p3=new Person("张三",18);

创建了p1和p3两个对象,他们的name和age都相同。按照我们使用equals的目的,他们的比较结果应该是相等的。这里显示的结果却是false。

我们在debug窗口中查看p1和p3的地址就可以发现,一个是470一个是471,而由于equals比较的是对象地址,所以二者并不相同。原因很明显,我们并没有告诉Java这个equals要比较哪些内容,所以它只会按照默认行为来比较地址。

但是我们的目的是为了判断两个对象内容是否相同,现在的equals违背了我们的意愿,应该如何解决呢?

答案就是重写equals方法。

我们先看下面这个例子:

public boolean equals(Person p){
        return (this.name.equals(p.name)) && (this.age==p.age);
}

 这里,我在类内“重写”了一个equals方法,然后让它分别比较name和age,如果都相等则返回true。否则返回false。(重写打了引号,并不是真正意义上Override的那个重写)

显然这个结果是符合我们期望的,但是它真的对吗?我们可以看一看Java中方法重写的规则:

在重写方法时,需要遵循下面的规则:

  • 参数列表必须完全与被重写的方法参数列表相同。
  • 返回的类型必须与被重写的方法的返回类型相同(Java1.5 版本之前返回值类型必须一样,之后的 Java 版本放宽了限制,返回值类型必须小于或者等于父类方法的返回值类型)。
  • 访问权限不能比父类中被重写方法的访问权限更低(public>protected>default>private)。
  • 重写方法一定不能抛出新的检査异常或者比被重写方法声明更加宽泛的检査型异常。例如,父类的一个方法声明了一个检査异常 IOException,在重写这个方法时就不能抛出 Exception,只能拋出 IOException 的子类异常,可以抛出非检査异常。

第一条便是:参数列表必须完全与被重写的方法参数列表相同。

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

这时我们再回头看equals方法的原型,发现我们写的equals方法与Object类中的方法参数列表并不相同,所以我们这并不是对equals方法的重写。而恰巧我们这样写的equals方法符合Java方法重载的要求,所以它也可以正常执行。(所以上文中我将“重写”打上了引号)

正确的重写方法应该如下所示:

@Override
    public boolean equals(Object o){
        if(!(o instanceof Person)){
            return false;
        }
        Person person=(Person) o;
        return this.name.equals(person.name) && (this.age==person.age);
    }

 在进行方法重写时,最好加上@Override关键字,这样编译器就会帮助你检查参数列表是否一致,避免产生潜在的错误。

为了保证参数列表一致,所以我们的equals方法参数列表必须是Object o。由于没有直接的Person对象,所以可以看到首先用instanceof 来判断是否属于Person这个类,不属于则直接返回false,如果是Person类则将Object o强转成Person类型,然后继续进行比较。

根据这一特点,我们便可以进行一些比较个性化的比较,比如上图只判断两个人名字是否相同。

另一种更便捷的重写equals方法的方式是利用IDEA自动生成。

在代码编辑界面点击鼠标右键,然后选择Generate...

 这里有非常多的内容可以选择,比如Setter或Getter等等,这里我们想要生成equals方法,所以选择equals() and hashCode()

 

然后按照它的提示,选择需要被包括的Fields即可自动生成代码:

这是在IDEA里非常方便而且准确的一种方法。

综上所述,当我们使用equals方法的时候一定要谨慎,不要想当然的认为他会比较对象内容是否相同。特别是我们自己封装的类,equals的默认行为是比较地址,使用的时候必须重写。另外还需要分清楚方法重写和方法重载。

对于一些其他方法,例如contains方法,如果不重写的话默认也是比较地址,使用的时候也需要注意到这一点,不要直接使用contains判断容器中是否含有具有某个属性的对象。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐