Java源码阅读------Object

描述

java中一切事物几乎都可以看作类的实例化对象,而所有的类都与Object息息相关,Object类是所有类的父类先祖,甚至是数组arrays都是要实现Object类中定义的方法,如果一个类没有别的继承声明那么这个类一定是继承了Object类。

相关实现

常用函数

equals

这是比较常用的一个方法,在源码中的实现也很简单。

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

通过传入的引用和自身比对,如果是对同一个对象的引用返回为真。这里引用是指实例化对象在堆中的地址。

toString

这个函数也是比较常用的,是用于对对象进行文字性描述的方法。我们在使用时一般是重载这个方法。

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

在Object中的直接使用时,返回的是对象运行时的类名与hashCode()获取的表示内存地址信息的值。比如

Object o=new Object();
System.out.println(o.toString());
//输出java.lang.Object@6b884d57

native方法

在java中有很多的方法不是完全使用java实现的,有些对系统底层的调用还需要使用C语言来实现,对这些函数的声明是用native来声明的。

registerNatives

Object类中也有许多的native方法,这些方法在初始化的时候需要将它们与对应的C语言方法对应,而实现这个过程的方法是registerNatives。

private static native void registerNatives();
static {
	registerNatives();
}

在类中对这一方法进行了初始的静态调用,对本地的方法进行注册,在其他类中也用类似的使用,比如System。
再往深一点看看,就是jni的实现了

static 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},
};

这里是将java中的函数与在C语言中的函数连接,左边是java中的函数名,右边是在C语言中的函数名,中间的像“()I”中的“I”是jni中对数据类型的转换,V是void,I是int,Object对应是Ljava/lang/Object。还有一点是其中并没有getClass这一函数,但是在调用时还是会按这个名称Java_java_lang_Object_getClass调用。
再看看详细的实现

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
}
//将本地的几个方法methods注册。

hashCode

public native int hashCode();

使用这一函数对不同的对象进行区分(为不同的对象返回不同的整数(原文)),一般由对象实例化后在堆中的地址转换为整数实现,不是由java自己实现(native)。
除此之外,该函数为有关哈希表的实现提供支持,比如java.util.HashMap。
这个函数可以与equals函数呼应,对于equals不相等的函数,使用hashCode得到的是不等的值。(对同一对象的引用,返回的是相等的地址信息,对不同对象的引用返回的是不同的)

clone

protected native Object clone() throws CloneNotSupportedException;

这一方法实现了对于已有对象的克隆,但是需要实现Cloneable接口,而且是浅拷贝,具体可以参照Java技巧------Cloneable接口与clone方法

并发与同步

synchronized大家应该不陌生,我们通过它来同步某个对象或是代码块,我们称之为锁,锁是干啥的呢?事实上在多线程中往往会出现多个线程对同一对象进行修改,但是我们无法实时更新变量的实际状况,大多数时候在我们刷新数据时,这个数据还在进行变化,最常见的就是买票这一类,因此某一数据在任意时刻仅允许一个线程对其进行更改即拿到了锁(拿到了就锁起来只能自己改),没有拿到的线程就无法进行操作即所谓得阻塞式访问。

wait(long)

这个方法有三种种形式,这个是基本的本地实现。

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

首先,调用这一方法的类已经被线程获取到了锁,正在进行相关的逻辑操作,但是由于设计原因,比如异步操作,一些逻辑需要等待一段时间进行时使用这一方法,使用时正在使用的线程放弃使用解锁等待并让出使用权,直到别的线程调用该类的notify或notifyAll方法或者超过了timeout设置的时间,亦或者其他的线程使用了interrupt()方法,再重新获得锁。参数是指超时时间,如果超时时间为0,则只能等着其他线程通知了。

wait(long, int)

这个函数用于更好的去控制超时时间,第二个参数为毫秒量。

public final void wait(long timeout, int nanos) throws InterruptedException {
	if (timeout < 0) {
		throw new IllegalArgumentException("timeout value is negative");
	}

	if (nanos < 0 || nanos > 999999) {
		throw new IllegalArgumentException("nanosecond timeout value out of range");
	}

	if (nanos > 0) {
		timeout++;
	}

	wait(timeout);
}
wait()

特殊情况,超时时间为0。

public final void wait() throws InterruptedException {
	wait(0);
}

notify

通过这一函数来通知等待操作的其他线程,如果有多个线程在等待就选取其中一个。之后,等到正在操作的对象放弃使用权解锁时,该线程运行并获得锁。

public final native void notify();

notifyAll

通过这一函数来通知等待操作的其他线程,之后,等到正在操作的对象放弃使用权解锁时,这些线程运行。

public final native void notifyAll();

强调

所谓唤醒与获得锁是两个概念,不是说notify就直接可以获得锁并执行,那样notifyAll会存在多个线程同时获得锁。
被wait的线程要先被唤醒,之后才能参与锁的争夺。
所以大多数使用是将wait写到循环里,防止判断条件被唤醒的线程打破。

反射

java中的反射,即指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。

getClass

public final native Class<?> getClass();

返回这一对象运行时的类Class(这里是指Class这个类)的实例。简单的举个例子。

Object o=new Object();
Object s= "123";
System.out.println(o.getClass().getName());
System.out.println(s.getClass().getName());
//输出
//java.lang.Object
//java.lang.String

返回的是运行时的类的实例,所以第二个是String,不是Object。

finalize

该方法不是什么理解上的析构函数,gc在回收对象之前会调用该函数。我们可以重写该函数实现对象回收前的资源清理。

protected void finalize() throws Throwable { }

Java无法保证finalize的及时执行,甚至不保证其执行,只是在回收之前调用,除此之外该方法由优先级较低的线程执行,gc至多自动处理一次。

小结

Object类还是很重要的,短短的几百行代码(含注释)中蕴含着许多的设计思想,作为众多类的祖先,其中的相关特性值得我们去深究。

猜你喜欢

转载自blog.csdn.net/sinat_36945592/article/details/87106073