如何正确的覆盖equals和hashCode

1、Object全部的非final方法

  1. public boolean equals(Object obj)
  2. public native int hashCode()
  3. public String toString()
  4. protected native Object clone() throws CloneNotSupportedException
  5. protected void finalize() throws Throwable { }

类的方法前加final关键字,说明该方法不能被该类的子类重写。ide

2、equals方法和hashCode方法

一、何时须要覆盖equals方法

若是类具备本身特有的“逻辑相等”概念(不一样于对象等同的概念),并且超类尚未覆盖equals以实现指望的行为,这时候咱们就须要覆盖equals方法。this

二、如何正确的覆盖equals方法

equals方法实现了等价关系:自反性,对称性,传递性,一致性,非空性(x.equals(null)返回为falsespa

  • 使用==操做符检查“参数是否为这个对象的引用”,若是是,则返回true。
  • 使用instanceof操做符检查“参数是否为正确的类型”,若是不是,则返回false。
  • 把参数转换成正确的类型。
  • 对于该类中的每一个“关键”阈,检查参数中的阈是否与该对象中对应的阈相匹配。
class Student{
    
    private String name;
    private int age;
    private double height; 
    @Override
    public boolean equals(Object obj) {
        //使用==操做符检查“参数是否为这个对象的引用”
        if(this==obj)
            return true;
        //使用instanceof操做符检查“参数是否为正确的类型”
        if(!(obj instanceof Student))
            return false;
        //把参数转换成正确的类型。
        Student student=(Student) obj;
        //对于该类中的每一个“关键”阈,检查参数中的阈是否与该对象中对应的阈相匹配。
        return  this.name==student.name && this.age==student.age && this.height==student.height;
    }
}

一、编写完成equals方法以后,应该问本身三个问题:它是否对称的、传递的、一致的。code

二、不要将equals声明中的Object对象替换为其余的类型对象

public boolean equals(Student obj)

这样至关于重载了equals方法,而非是覆盖。blog

三、覆盖equals时总要覆盖hashCode

HashCode有一条约定以下:递归

若是两个对象根据equals(Object)方法比较是相等,那么调用这两个对象中任意一个对象的hashCode方法都必须产生一样的整数结果。string

下面给出一种简单的解决办法:hash

  1. 把某个非零的常数值,好比说17,保存在一个名为result的int类型的变量中。
  2. 对于对象每一个关键域f(指equals方法中涉及的每一个域),完成如下步骤:
    1. 若是该域是boolean类型,则计算(f ? 1 : 0)。
      private boolean flag=true;
      int boolTemp=flag?0:1;
    2. 若是该域是byte、char、short或者int类型,则计算(int)f。
    3. 若是该域是float类型,则计算Float.floatToIntBit(f)。
    4. 若是该域是long类型,则计算(int)(f ^ (f >>> 32))。
    5. 若是该域是double类型
      private double height; 
      long heightBits=Double.doubleToLongBits(height);
      int heightTemp=(int)(heightBits ^ (heightBits >>> 32));
    6. 若是该域是一个对象引用,而且该类的equals方法经过递归调用equals的方式来比较这个域,则一样为这个域递归调用hashCode。
      private String name;
      int stringTemp=this.name.hashCode();

完整的Student类:it

class Student{
    private String name;
    private int age;
    private double height; 
    @Override
    public boolean equals(Object obj) {
        //使用==操做符检查“参数是否为这个对象的引用”
        if(this==obj)
            return true;
        //使用instanceof操做符检查“参数是否为正确的类型”
        if(!(obj instanceof Student))
            return false;
        //把参数转换成正确的类型。
        Student student=(Student) obj;
        //对于该类中的每一个“关键”阈,检查参数中的阈是否与该对象中对应的阈相匹配。
        return  this.name==student.name && this.age==student.age && this.height==student.height;
    }
    
    @Override
    public int hashCode() {
        //初始化
        int result=17;
        //String类型
        result=this.name.hashCode()+result;
        //int类型
        result=this.age+result;
        //double类型
        long heightBits=Double.doubleToLongBits(height);
        int heightTemp=(int)(heightBits ^ (heightBits >>> 32));
        result=heightTemp+result;
        //返回
        return result;
    }
}