JDK源码阅读之Object

Object

      JAVA中Object是所有类的超类,也就是说一切对象都是继承自Object类。我们从这里迈入JDK源码的大门。

Object中的方法一览

private static native void registerNatives();
public final native Class<?> getClass();
public native int hashCode();
public boolean equals(Object obj);
protected native Object clone() throws CloneNotSupportedException;
public String toString();
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
public final void wait() throws InterruptedException;
protected void finalize() throws Throwable;

registerNatives()

      我们可以发现这个方法使用了native进行修饰,这表明这是一个本地方法,所谓的本地方法是不通过Java语言实现的方法,但可以通过JNI像调用Java方法一样调用这些方法。底层是使用C、C++来实现的,我们可以通过查看OpenJDK查看底层源码:

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&amp;JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&amp;JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&amp;JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&amp;JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&amp;JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

      这部分代码实现对本地方法的注册(即初始化java方法映射到C的方法)。具体的知识请学习JNI。

getClass()

      返回运行时对象的Class。在反射中经常使用。

hashCode()

      返回对象的哈希码值。hashCode()方法在hash table和hash map等容器中尤为重要。使用hashCode()方法判断Key是否相同(事实上没那么简单,涉及解决冲突),或者迅速找到对应的元素。

hashCode方法的约定:

  1. 在应用程序执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一的返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
  2. 如果两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
  3. 如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生不同的整数结果。(尽管如此,理想的hashCode方法对不相等的对象,应当提供不同的hashCode值,这样当我们将对象作为Map的key时,可以提高散列表的性能)

一般情况下子类都会重写hashCode()方法,比如String类的hashCode()方法如下:

public int hashCode() {
    int h = hash;
    if (h == 0 &amp;&amp; value.length > 0) {
         char val[] = value;
         for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
         }
         hash = h;
     }
     return h;
}

      可以发现空串的哈希码为0。

equals()

      返回 2个对象的内存地址是否相等。在Object中的实现为:

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

      ==表示的是变量值完成相同(对于基础类型,地址中存储的是值,引用类型则存储指向实际对象的地址)。默认equals()方法比较obj的地址是否等于自身地址。大部分情况下我们使用equals()比较的不仅仅判断地址是否相同,更应该判断对象中的内容是否相同。比如String类比较每个字符是否相同。

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        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;
}

      首先判断地址是否相同,地址相同就是一个对象嘛!如果地址不同,逐一判断每个字符是否相同。

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)返回true,那么x.equals(z)也必须返回ture。
  4.  一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回ture,或者一致的返回false。
  5. 非空性:对于任何非null的引用值x,x.equals(null)必须返回false。

      注意:当我们覆盖了equals方法时,一定不能忘记覆盖hashCode方法

clone()

      创建并且返回对象的拷贝。对象重写clone()方法必须实现Cloneable接口,否则抛出java.lang.CloneNotSupportedException。注意:使用clone()时需要注意深拷贝和浅拷贝的问题。

toString()

      默认返回:getClass().getName() + "@" + Integer.toHexString(hashCode()); 当打印对象时会自动调用toString()方法。为了打印美观我们一般都会自己实现此方法。

finalize()

      垃圾回收器准备释放内存的时候,会先调用finalize()。对于任何给定对象,Java 虚拟机最多只调用一次finalize方法。我们需要注意的是GC时不一定马上调用此方法。大部分情况下不建议使用finalize()方法。

wait()

public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
public final void wait() throws InterruptedException;

      wait()方法是不允许重写的。

      当线程调用wait()方法时,释放锁,进入阻塞状态,等待被唤醒。

  1. wait():当前线程阻塞,直到被唤醒。
  2. wait(long time):当前线程阻塞,直到其他线程调用此对象的 notify()方法或 notifyAll()或超过设定时间
  3. wait(long timeout, int nanos):当前线程阻塞导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

notify()

      唤醒在此对象监视器上等待的单个线程。

notifyAll()

      唤醒在此对象监视器上等待的所有线程。

      关于这几个多线程方法以后会再次接触,此处只是简单了解作用。

Last modification:July 10th, 2019 at 08:05 pm
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment