首页 > 代码库 > 11.Object方法

11.Object方法

综述

Object是Java中所有类的父类,对它的学习十分的重要, Object的函数除了final方法,基本上都是被设计为要被覆盖的(Override),这节我们就一起来学习这些函数。



1.equals函数

/*equals的源代码*/
public
boolean equals(Object obj) { return (this == obj); }

 从源代码中我们可以看出来,不重写equals函数的话,一个对象只会与它本身相等,因此对于"值类"(String、Integer等)我们往往需要覆盖其equals函数。

覆盖equals时需要保证的几个条件

  1. 自反性,对于任何非null的引用值x,x.equals(x)必须反回true 
  2. 对称性,对于任何非null的引用值x,y,当且仅当y.equals(x)返回true时,x.equals(y),才返回true. 
  3. 传递性,对于任何非null的引用值x,y,z,如果x.equals(y)返回true,并且y.equals(z)返回ture,那 么x.equals(z)也必须返回true. 
  4. 一致性,对于任何非null的引用值x,y,只要equals比较操作的两个对象中所用的信息没有被修改,多次 调用x.equals(y)就会一致的返回true,或者一致的返回false. 
  5. 对于任何的非null的值x,x.equals(null),必须返回false 

覆盖equals时必须要覆盖hashCode函数

Java中有如下的"约定"

equals()返回true------------------->hashCode()值相等//这个也是覆盖equals函数必须覆盖hashCode函数的原因之一
hashCode值相同------推不出--------equals()结果
a==b              --------------------->equals()值为true
instanceOf返回false------------------>equals()值为false

 重点:只覆盖equals函数不覆盖hashCode函数会有什么问题?

final class PhoneNumber{
    private final int areaCode;
    private final int prefix;
    private final int lineNumber;

    public PhoneNumber(int areaCode,int prefix,int lineNumber){
        this.areaCode=areaCode;
        this.prefix=prefix;
        this.lineNumber=lineNumber;
    }

    @Override
    public boolean equals(Object obj){
        if(obj==this) return true;
        if(!(obj instanceof PhoneNumber)) return false;
        PhoneNumber o=(PhoneNumber) obj;
        return o.lineNumber==this.lineNumber
                  &&o.prefix==this.prefix
                  &&o.areaCode==this.areaCode;
    }
}

main 函数
public static void main(String[] args) {
        Map<PhoneNumber,String> map=new HashMap<>();
        map.put(new PhoneNumber(1,2,10),"AAA");
        map.put(new PhoneNumber(4,6,20),"BBB");

        System.out.println(new PhoneNumber(1,2,10).equals(new PhoneNumber(1,2,10)));
        System.out.println(new PhoneNumber(1,2,10).hashCode()==new PhoneNumber(1,2,10).hashCode());
        System.out.println(map.get(new PhoneNumber(1,2,10)));
    }
    

 答案和解析

这段代码有三个输出流,而且PhoneNumber类并没有覆盖hashCode函数

1.验证equals函数,输出true,可以看出没有覆盖hashCode函数对equals没有什么影响

2.验证hashCode函数,由于没有覆盖HashCode函数,调用的是Object类的hashCode函数,两个类就算值是相同的,hashCode值也是不一样的,输出为false

3.Map中插入了一条数据key:new PhoneNumber(1,2,10) value:AAA,但是使用get函数的时候却找不到这个值了(输出null),这是因为没有遵循java中重写equals不重写hashCode函数的原因。

具体的原因:

技术分享

                                           图1.HashMap结构图

HahMap底层是一个数组Entry[],每个数组元素是一个单链表,插入数据和获取数据的过程基本如下(过滤掉细节)

a.插入数据过程

  • 根据Key值计算数据下标index,HashMap中index的计算是这样的,index=key.hashCode()^value.hashCode()   //省去进行算法数运算
  • 计算出下标之后在到对应的单链表中进行查找key,找到了就替换value,找不到就插入k-V

 b.查找数据过程

  • 计算index,index=key.hashCode()^value.hashCode(),但是这里的Key.hashCode值与插入的时候由于是不同的对象,所以hashCode值是不一样的
  • index不一样,所以在对应的单链表中就找不到对应的数据

结论:

1.遵循Java规范,覆盖equals()时应该覆盖hashCode()的值

2.覆盖hashCode之后输出结果改变

 @Override
    public int hashCode(){
        return this.areaCode^this.prefix^this.lineNumber;
    }
//输出结果
true
true
AAA

 2.HashCode函数

/*Object 中的hashCode函数*/
public native int hashCode();
//是一个native函数,也就是不是用Java开发的

 问题:它是怎么编写的?----先留着

2.1HashCode函数的编写规范

  • 可用性--"相同"的对象的hashCode值是相同的
  • 说明:这个hashCode是可用的,但是所有对象的hashCode值都是一样的,散列表就回退化为单链表
@Override
public int hashCode(){
        return 1;
}
  • 高质量的HashCode要求相同的对象HashCode相同,不相同的对象的HashCode不相同-------怎么设计感觉是一个难题
  • 对于散列表来说,希望散列表各个分支的长度相差不大
  • 对于原始的HashCode函数
  • 有些朋友误以为默认情况下,hashCode返回的就是对象的存储地址,事实上这种看法是不全面的,确实有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联。下面是HotSpot JVM中生成hash散列值的实现:
  • static inline intptr_t get_next_hash(Thread * Self, oop obj) {
      intptr_t value = http://www.mamicode.com/0 ;"invariant") ;
      TEVENT (hashCode: GENERATE) ;
      return value;
    }
    

     

 

11.Object方法