java重写equals方法需要注意的几点

为什么equals()方法要重写?

判断两个对象在逻辑上是否相等,如根据类的成员变量来判断两个类的实例是否相等,而继承Object中的equals方法只能判断两个引用变量是否是同一个对象(即object的equals比较的是地址值)。这样我们往往需要重写equals()方法。

string类型已经重写了equals方法,从头比较字符;

我们向一个没有重复对象的集合中添加元素时,集合中存放的往往是对象,我们需要先判断集合中是否存在已知对象,这样就必须重写equals方法。

怎样重写equals()方法?

重写equals方法的要求:
1、自反性:对于任何非空引用x,x.equals(x)应该返回true。
2、对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
3、传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
4、一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
5、非空性:对于任意非空引用x,x.equals(null)应该返回false。

 

1、自反性原则

    在JavaBean中,经常会覆写equals方法,从而根据实际业务情况来判断两个对象是否相等,比如我们写一个person类,根据姓名来判断两个person类实例对象是否相等。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public  class  Person {
     private  String name;
  
     public  Person(String name){
         this .name = name;
     }
  
     public  String getName() {
         return  name;
     }
  
     public  void  setName(String name) {
         this .name = name;
     }
  
     @Override
     public  boolean  equals(Object obj) {
         if  (obj  instanceof  Person) {
             Person person= (Person) obj;
             return  name.equalsIgnoreCase(person.getName().trim());
         }
         return  false ;
     }
     public  static  void  main(String[] args){
         Person p1= new  Person( "张三" );
         Person p2= new  Person( "张三    " );
         List<Person> list =  new  ArrayList<Person>();
         list.add(p1);
         list.add(p2);
         System.out.println( "是否包含张三:" +list.contains(p1));
         System.out.println( "是否包含张三:" +list.contains(p2));
     }
}

    list中含有这个生成的person对象,结果应该为true,但是实际结果:    这里考虑了字符串空格的问题,去除前后的空格。

    是否包含张三:true

    是否包含张三:false

    第二个为什么会是false呢?原因在于list中检查是否含有元素时是通过调用对象的equals方法来判断的,也就是说 contains(p2)传递进去会依次执行p2.equals(p1)、p2.equals(p2),只要一个返回true,结果就是true。但是这里p2.equals(p2)返回的是false?由于我们对字符前后进行了空格的切割造成p2.equals(p2)的比较实际上是:“张三   ”.equals(“张三”),一个有空格,一个没有空格就出错了。

    这个违背了equals的自反性原则:对于任何非空引用x,x.equals(x)应该返回true。

    这里只要去掉trim方法就可以解决。

2、对称性原则

    上面这个例子,还并不是很好,如果我们传入null值,会怎么样呢?增加一条语句:Person p2=new Person(null);

结果:

1
2
是否包含张三:true
Exception in thread "main" java.lang.NullPointerException

原因在执行p2.equals(p1)时,由于p2的name是一个null值,所以调用name.equalsIgnoreCase()方法时就会报空指针异常

这是在覆写equals方法时没有遵循对称性原则:对于任何应用x,y的情形,如果想x.equals(y)返回true,那么y.equals(x),也应该返回true。

应该在equals方法里加上是否为null值的判断:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
     public  boolean  equals(Object obj) {
         if  (obj  instanceof  Person) {
             Person person= (Person) obj;
             if  (person.getName() ==  null  || name ==  null ) {
                 return  false ;
             } else {
                 return  name.equalsIgnoreCase(person.getName());
             }
         }
         return  false ;
     }

3、传递性原则  

现在我们有一个Employee类继承自person类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public  class  Employee  extends  Person{
     private  int  id;
  
  
     public  int  getId() {
         return  id;
     }
     public  void  setId( int  id) {
         this .id = id;
     }
     public  Employee(String name, int  id) {
         super (name);
         this .id = id;
         // TODO Auto-generated constructor stub
     }
     @Override
     public  boolean  equals(Object obj) {
         if (obj  instanceof  Employee){
             Employee e = (Employee)obj;
             return  super .equals(obj) && e.getId() == id;
         }
         return  super .equals(obj);
     }
  
     public  static  void  main(String[] args){
         Employee e1= new  Employee( "张三" , 12 );
         Employee e2= new  Employee( "张三" , 123 );
         Person p1 =  new  Person( "张三" );
  
         System.out.println(p1.equals(e1));
         System.out.println(p1.equals(e2));
         System.out.println(e1.equals(e2));
     }
}

  只有在name和ID都相同的情况下才是同一个员工,避免同名同姓的。在main里定义了,两个员工和一个社会闲杂人员,虽然同名同姓但肯定不是同一个人。运行结果应该三个都是false才对。但是:

true

true

false

    p1尽然等于e1,也等于e2,不是同一个类的实例也相等了?因为p1.equals(e1)是调用父类的equals方法进行判断的它使用instanceof关键字检查e1是否是person的实例,由于employee和person是继承关系,结果就是true了。但是放过来就不成立,e1,e2就不等于p1,这也是违反对称性原则的一个典型案例。

   e1竟然不等于e2?e1.equals(e2)调用的是Employee的equals方法,不仅要判断姓名相同还有判断工号相同,两者的工号不同,不相等时对的。但是p1等于e1,也等于e2,e1却不等于e2,这里就存在矛盾,等式不传递是因为违反了equals的传递性原则:对于实例对象x、y、z;如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。

    上述情况会发生是因为父类使用instanceof关键字(是否是这个特定类或者是它的子类的一个实例),用来判断是否是一个类的实例对象的,这很容易让子类“钻空子”。想要解决也很简单,使用getClass进行类型的判断,person类的equals方法修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
     public  boolean  equals(Object obj) {
         if  (obj !=  null  && obj.getClass() ==  this .getClass()) {
             Person person= (Person) obj;
             if  (person.getName() ==  null  || name ==  null ) {
                 return  false ;
             } else {
                 return  name.equalsIgnoreCase(person.getName());
             }
         }
         return  false ;
     }


4、必须覆写hashCode方法
这样结果就是三个false。

覆写equals方法就必须覆写hashCode方法,这是Javaer都知道的。原因就是HashMap的底层处理机制是以数组的方式保存map条目的,这其中的关键是这个数组下标的处理机制:依据传入元素的hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到map条目的链表中。同理检查键是否存在也是根据哈希吗确定文职,然后遍历查找键值的。

那么对象的hashCode方法返回的是什么呢?他是一个对象的哈希码,是有Object类的本地方法生成的,确保每个对象有一个哈希码。

具体的可以看另一篇博文:http://www.cnblogs.com/silence-hust/p/4510574.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值