Java—equals方法小结
equals在字面意思上与“==”功能似乎一致,都是“相等”的意思。但在Java实际开发中两者有着很大不同。
1.功能一、字符串的比较
equals方法属于Object类,在该类中我们可以找到源码。
equlas方法在源码中定义如下:
public boolean equals(Object obj) {
return (this == obj);
}
从源码可以看出,虽然也用到了“==”运算符,但equals是通过this指针,比较对象的地址值,而并非直接比较数值。
通过下面的一个例子我们可以看出区别:
public class ComPare {
public static class Sales{
private String name;
private boolean Judge1(String s1, String s2){
//对两个字符串进行equals比较
if(s1.equals(s2)) return true;
return false;
}
private boolean Judge2(String s1,String s2){
//对两个字符串进行运算符比较
if(s1==s2) return true;
return false;
}
private boolean Judge3(Sales s1,Sales s2){
//对两个字符属性进行运算符比较
if(s1.name==s2.name) return true;
return false;
}
private boolean Judge4(Sales s1,Sales s2){
//对两个字符属性进行equals比较
if(s1.name.equals(s2.name)) return true;
return false;
}
public static void main(String[] args) {
Sales s1=new Sales();
Sales s2=new Sales();
s1.name="Mike";
s2.name="Mike";
String s3,s4;
s3=new String("Nick");
s4=new String("Nick");
if(s1.Judge1(s3, s4)) System.out.println("正确!");
else System.out.println("错误!");
if(s1.Judge2(s3, s4)) System.out.println("正确!");
else System.out.println("错误!");
if(s1.Judge3(s1, s2)) System.out.println("正确!");
else System.out.println("错误!");
if(s1.Judge4(s1, s2)) System.out.println("正确!");
else System.out.println("错误!");
}
}
}
运行结果如下:
正确!
错误!
正确!
正确!
由第一、第二个输出结果可见,由于字符串s1和字符串s2的数值相等,但存放在内存中的地址值不等,故当运用到“”运算符时,boolean类型会返回False。
那么为什么字符串变量作为对象的属性时,“”和equals的返回值又会相同呢?这便涉及到了JVM中内存管理的问题,二者在存放时位于JVM的同一块内存区域,故地址值相等。在此不作赘述。
同时还需注意的是:如果是基本类型比较,那么只能用==来比较,不能用equals。即如果这么用的话,编译是无法通过的。
一个需要注意的“BUG”
我们来看经典《Java编程思想》中的一段代码:
class Value {
int i;
}
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
System.out.println(v1.equals(v2));//flase
System.out.println(v1 == v2);//false
}
}
为什么第一个输出False呢?
原来,如果在新类中被覆盖了equals方法,在此确实是用来比较内容的。但是在上面的例子中类Value并没有覆盖Object中的equals方法,而是继承了该方法,因此它就是被用来比较地址的,又v1和v2的所指向的对象不相同,故两行输出结果均为false。
2.功能二、方法重载
我们再在ide中直接调用equals方法:
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
由于Object类可以说是整个JDK中“祖宗”级别的存在,对于“==”号,我们没办法像C++那样通过运算符重载来实现重写。但由于equals是一个方法,我们可以通过重写实现方法的重载。
这也引出了equals的第一个功能:
方法可重载。
下面我们来举一个Java实例:
比如我们要判断两个学生是否是同一个,由于存在着同名的情况,因此我们需要同时对学生的学号进行判断:
@Override
public boolean equals(Object obj) {
//此处采取了工厂模式创造对象
if (this==obj) return true;
else if (obj==null||getClass()!=obj.getClass()) return false;
Stu stu=(Stu) obj;
return id == stu.id && (Objects.equals(name,stu.name));
}
运行整个程序:
import java.util.Objects;
public class Stu{
private String name;
private int id;
@Override
public boolean equals(Object obj) {
//此处采取了工厂模式创造对象
if (this==obj) return true;
else if (obj==null||getClass()!=obj.getClass()) return false;
Stu stu=(Stu) obj;
return id == stu.id && (Objects.equals(name,stu.name));
}
public boolean Judge(Stu stu1,Stu stu2){
if (stu1.equals(stu2)) return true;
else return false;
}
public static void main(String[] args) {
Stu stu1=new Stu();
Stu stu2=new Stu();
Stu stu3=new Stu();
stu1.id=101;
stu2.id=102;
stu3.id=101;
stu1.name="James";
stu2.name="James";
stu3.name="James";
if(stu1.Judge(stu1, stu2)) System.out.println("是同一个学生");
else System.out.println("不是同一个学生");
if(stu1.Judge(stu1, stu3)) System.out.println("是同一个学生");
else System.out.println("不是同一个学生");
}
}
程序运行结果:
不是同一个学生
是同一个学生
可见,我们可以通过类似重载运算符的方法,对equals类进行重写,从而满足我们的各种功能需要。
equals重写的限制
通过查阅JDK,我们可以发现,equals在重写时是有限制的(在此偷个懒,抠的是别人翻译好的图 ):
拓展:equals重写与hashcode
由于hash在Java内存管理和数据查询中起着非同一般的作用,因此每当我们提到地址时,我们便不得不提到hash和hashcode。
关于Java中的hash原理、运用,在此不是本文的重点。在此只讨论hashcode和equals的关系。
由于hashcode是由数值经过hash算法得来的,所以有这么一条结论:
equals相等,hashcode相等;但hashcode相等,equals不一定相等
且我们还需要注意:
equals重写后,hashcode方法通常也需要重写
此结论在此亦不作赘述,大家可在运用HashMap时体会。
总结
1.equals是Object类的成员方法,本身比较的是“值”,而“==”运算符比较的是地址。但在使用时,需要把握好类的继承与覆盖的区别;
2.equals的一大好处在于可被重写,但在与hashcode结合运用时需要注意最好同时也重写hashcode。