zoukankan      html  css  js  c++  java
  • 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函数的原因。

    具体的原因:

    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 = 0 ;
        if (hashCode == 0) {
           // This form uses an unguarded global Park-Miller RNG,
           // so it's possible for two threads to race and generate the same RNG.
           // On MP system we'll have lots of RW access to a global, so the
           // mechanism induces lots of coherency traffic.
           value = os::random() ;
        } else
        if (hashCode == 1) {
           // This variation has the property of being stable (idempotent)
           // between STW operations.  This can be useful in some of the 1-0
           // synchronization schemes.
           intptr_t addrBits = intptr_t(obj) >> 3 ;
           value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
        } else
        if (hashCode == 2) {
           value = 1 ;            // for sensitivity testing
        } else
        if (hashCode == 3) {
           value = ++GVars.hcSequence ;
        } else
        if (hashCode == 4) {
           value = intptr_t(obj) ;
        } else {
           // Marsaglia's xor-shift scheme with thread-specific state
           // This is probably the best overall implementation -- we'll
           // likely make this the default in future releases.
           unsigned t = Self->_hashStateX ;
           t ^= (t << 11) ;
           Self->_hashStateX = Self->_hashStateY ;
           Self->_hashStateY = Self->_hashStateZ ;
           Self->_hashStateZ = Self->_hashStateW ;
           unsigned v = Self->_hashStateW ;
           v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
           Self->_hashStateW = v ;
           value = v ;
        }
       
        value &= markOopDesc::hash_mask;
        if (value == 0) value = 0xBAD ;
        assert (value != markOopDesc::no_hash, "invariant") ;
        TEVENT (hashCode: GENERATE) ;
        return value;
      } 


     3.toString函数

    /*Object类的toStrign函数*/
    public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
    //toString函数默认输出的是类名+@+hashCode()

     建议每个字类都重写toString函数,并且建立相关的文档 



    4.谨慎的覆盖clone

    /*Object中的clone函数*/
    protected
    native Object clone() throws CloneNotSupportedException;

    4.1 数组是支持clone()操作的

    利用这个函数我们可以复制一个"独立"的数组

    int[] arrays=new int[]{1,2,3,4};
    int[] copyArray=arrays.clone();//这里好像自动转换类型了?
    System.out.println(Arrays.toString(copyArray));
    这里独立的意思是这两个数组的地址是不一样的,如果这样写就不是独立的数组,int[] refArray=arrays;只是原来的数组上加了一个指针。

     4.2 覆盖clone()函数需要注意的地方

    1.必须要实现Clonable接口,不然就算覆盖了clone()函数,也会抛出ClonenotSupportException

    我们奇怪的发现Cloneable竟然是空的,那么我们为什么要实现Cloneable接口呢?-----参考文献1

    其实Cloneable接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。

    2.深层次的拷贝与潜层次拷贝

    • 浅克隆
    • 只能复制变量,对于对象,只复制他的引用
    • public class TaskTimeoutDemo extends Object implements Cloneable{
      public static void main(String[] args) {
      Inner inner=new Inner(22);
      MyClass myclass=new MyClass(20,"yangyun",inner);
      try {
      MyClass copyOfClass=myclass.clone();
      inner.setNum(90);
      System.out.println(copyOfClass);
      System.out.println(myclass);
      } catch (CloneNotSupportedException e) {
      e.printStackTrace();
      }
      }
      }

      class MyClass implements Cloneable{
      public int age;
      public String name;
      public Inner inner;

      public MyClass(int age,String name,Inner inner){
      this.age=age;
      this.name=name;
      this.inner=inner;
      }

      @Override
      public MyClass clone() throws CloneNotSupportedException{
      MyClass copyOfMyClass=(MyClass) super.clone();
      return copyOfMyClass;
      }

      @Override
      public String toString(){
      return super.toString()+" ["+age+":"+name+inner+"]";
      }
      }

      class Inner implements Cloneable{
      private int num;

      public Inner(int num){
      this.num=num;
      }

      public void setNum(int num) {
      this.num = num;
      }

      @Override
      public String toString(){
      return String.format("%d ",num);
      }
      }
      输出结果:

      MyClass@4deb1dbd [20:yangyun90 ]
      MyClass@77700f3d [20:yangyun90 ]

    • 深克隆
    • 对于对象,也完整的复制一份
    • public class TaskTimeoutDemo extends Object implements Cloneable{
          public static void main(String[] args) {
              Inner inner=new Inner(22);
              MyClass myclass=new MyClass(20,"yangyun",inner);
              try {
                  MyClass copyOfClass=myclass.clone();
                  inner.setNum(90);
                  System.out.println(copyOfClass);
                  System.out.println(myclass);
              } catch (CloneNotSupportedException e) {
                  e.printStackTrace();
              }
          }
      }
      
      class MyClass implements Cloneable{
          public int age;
          public String name;
          public Inner inner;
      
          public MyClass(int age,String name,Inner inner){
              this.age=age;
              this.name=name;
              this.inner=inner;
          }
      
          @Override
          public MyClass clone() throws CloneNotSupportedException{
              MyClass copyOfMyClass=(MyClass) super.clone();
              copyOfMyClass.inner=inner.clone();
              return copyOfMyClass;
          }
      
          @Override
          public String toString(){
              return super.toString()+" ["+age+":"+name+inner+"]";
          }
      }
      
      class Inner implements Cloneable{
          private int num;
      
          public Inner(int num){
              this.num=num;
          }
      
          public void setNum(int num) {
              this.num = num;
          }
      
          @Override
          public Inner clone(){
              Inner copyOfInner=null;
              try {
                  copyOfInner = (Inner) super.clone();
              }catch (CloneNotSupportedException e){
                  e.printStackTrace();
              }
              return copyOfInner;
          }
          
          @Override
          public String toString(){
              return String.format("%d ",num);
          }
      }
      //输出结果
      MyClass@27abcd5e [20:yangyun22 ]
      MyClass@28d2e37 [20:yangyun90 ]
      

    参考文献

    1.http://www.cnblogs.com/gw811/archive/2012/10/07/2712252.html

  • 相关阅读:
    sql server日志已满报错
    图片基础信息
    android小细节
    内存泄露分析
    一个非常快的android模拟器
    activity退出
    ListView中内容的动画效果
    视频相关android软件
    Android Screen Monitor抓取真机屏幕
    ListView中使用type需要注意的东西
  • 原文地址:https://www.cnblogs.com/yangyunnb/p/6082633.html
Copyright © 2011-2022 走看看