Object类分析(equals和hashCode知识点)

所有的java对象都隐式继承了Object类对象。所有的java对象都拥有Object默认的方法。

public final native ClassgetClass();//返回字节码文件对象 java反射实现方式之一
 public native int hashCode();
public boolean equals(Object obj)
protected Object clone();
public String toString();
public final void notify();
public final void notifyAll();
public final void wait(long timeout);
protected void finalized();

1.equals和hashCode

查看完文档以后我们查看源码,发现hashCode是由native关键字修饰,equals方法则是直接使用==比较了内存地址。

public boolean equals(Object obj) {
        return (this == obj);
    }

equals和hashCode用来标识对象,可以在非排序的情况下比较两个对象是否相等(对象数组中可以使用比较器)。

那么我们仔细关注一下hashCode源码所给出的注释

   /**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * 

* The general contract of {@code hashCode} is: *

    *
  • Whenever it is invoked on the same object more than once during * an execution of a Java application, the {@code hashCode} method * must consistently return the same integer, provided no information * used in {@code equals} comparisons on the object is modified. * This integer need not remain consistent from one execution of an * application to another execution of the same application. *
  • If two objects are equal according to the {@code equals(Object)} * method, then calling the {@code hashCode} method on each of * the two objects must produce the same integer result. *
  • It is not required that if two objects are unequal * according to the {@link java.lang.Object#equals(java.lang.Object)} * method, then calling the {@code hashCode} method on each of the * two objects must produce distinct integer results. However, the * programmer should be aware that producing distinct integer results * for unequal objects may improve the performance of hash tables. *
*

* As much as is reasonably practical, the hashCode method defined by * class {@code Object} does return distinct integers for distinct * objects. (This is typically implemented by converting the internal * address of the object into an integer, but this implementation * technique is not required by the * Java™ programming language.)

  1. 返回一个哈希值给对象,这个方法有益于底层结构是哈希表的结构对象,例如HashMap
  2. 如果两个对象的equals的结果是相等的,则调用hashCode返回的int也必须是相同的
  3. 如果两个对象的equals不相等,hashCode可以不相等,但是如果equasl不相等,hashCode也不相等的话,有利于提高散列性能。(Map集合和Set集合底层判断重复的时候,先判断hashCode是否相等,如相等,再判断key的equals是否相等,一旦短路与&&生效,会大大提高程序执行效率。)
  4. hashCode默认是由对象的地址转换而来,同时根据不同的对象转成不同的hash值(但这种实现不是java语言要求的 所以我们常常重写它)

适用场景:我们在适用自定义对象作为Map/Set键时,为了区分不同对象,必须重写hashCode和equals。

延申:equals和==的区别:
对于基本类型而言,==比较的是内容。
对于引用数据类型而言,==比较的是地址。
对于引用数据类型(String,Integer,date等),重写了equals和hashCode的,比较的就是内容。
对于引用数据类型,没有重写equals和hashCode的,适用的还是Object的equals方法,比较的是地址。

再延申:那我们就去看一看String重写的equals源码吧:

 public boolean equals(Object anObject) {
        if (this == anObject) {//先判断地址是否相等,地址相等直接返回
            return true;
        }
        if (anObject instanceof String) {//判断是否是String类,为了向下转型的安全
            String anotherString = (String)anObject;
            int n = value.length;//value就是本String转化成的字符数组
            if (n == anotherString.value.length) {//比较两者的字符数组是否相等
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {//从前往后一个字符一个字符比较
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

OK,那么我们知道了,String先是判断两个对象的地址是否相等,然后向下转型,再转成字符数组,从后向前遍历比较。

2.toString

    /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * 

* The {@code toString} method for class {@code Object} * returns a string consisting of the name of the class of which the * object is an instance, the at-sign character `{@code @}', and * the unsigned hexadecimal representation of the hash code of the * object. In other words, this method returns a string equal to the * value of: *

*
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * 
* * @return a string representation of the object. */ public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
  1. 用文本方式标识一个对象
  2. Object默认返回的是 字节码文件的名称+@+一个内存地址的int映射

clone()方法

一般java的赋值是复制对象的引用(=),(类初始化状态不一)浅拷贝。要实现深拷贝,(连成员变量初始化状态都一样)成员变量都拷贝出去一份(如果是可变的引用),因而"="是属于浅拷贝。
所以深拷贝是成员变量(如果是可变的引用)都复制一份,浅拷贝则是不复制成员变量。两者初始化程度不一。

clone用法:

  1. 克隆的对象要实现Cloneable接口
  2. 重写clone方法,最好修饰成public

范例:浅拷贝Time

public class Time implements Cloneable{

    //可变成员变量
    private Date date;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

范例:深拷贝Time

public class Time implements Cloneable{

    //可变成员变量
    private Date date;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //向下转型 拷贝Time对象
        Time time=(Time)super.clone();

        //拷贝可变的成员变量
        time.date=(Date)date.clone();

        //返回拷贝的对象
        return time;
    }
}

wait和notify方法

这是线程间通信的API

  1. 无论是wait、notify还是notifyAll()都需要由监听器对象(锁对象)来进行调用,他们都是在同步代码块中调用的,否则会抛出异常!
  2. notify()唤醒的是在等待队列的某个线程(不确定会唤醒哪个),notifyAll()唤醒的是等待队列所有线程
  3. 导致wait()的线程被唤醒可以有4种情况
  • 该线程被中断
  • wait()时间到了
  • 被notify()唤醒
  • 被notifyAll()唤醒
  1. 调用wait()的线程会释放掉锁

一些面试题:
为什么wait和notify在Object方法上?
因为锁是对象锁,让线程等待某个对象的锁,应该由对象来操作

notify方法调用后,会发生什么?
会唤醒等待队列的某个线程
注:并不会立刻唤醒,会等notify的synchronized代码块执行完之后才会获得锁对象

Thread.sleep和Object.wait区别?
sleep不会释放对象锁的控制,而wait会。

finalize方法

这是在GC前会被JVM调用的方法,用来对一些特定的内存进行GC,一般不重写,对一些JNI操作的gc会使用。

因而hashCode和equals用于对象比较,clone用于对象克隆,toString用于对象标识,notify与wait是对象锁相关。

你可能感兴趣的:(Object类分析(equals和hashCode知识点))