头像

带翅膀的猫

时光荏苒,我们一直都在

《JDK源码之Object》

 2月前  •   JDK源码阅读  •     •   17  •   0

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查看底层源码:

Cstatic JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&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()方法如下:

JAVA
public int hashCode() {
    int h = hash;
    if (h == 0 && 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中的实现为:

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

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

JAVApublic 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()

JAVA
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()

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

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

没有了,已经是最后文章啦
下一篇:

 评论


 已有0条评论

    还没有任何评论,你来说两句吧!