首页 > 编程 > Java > 正文

源码阅读笔记:java.lang.Object

2019-11-08 01:32:01
字体:
来源:转载
供稿:网友

java.lang.Object是Java中所有类的父类(super class),是唯一一个没有父类的Java类。所有对象都会自动继承Object,通过Object可以了解一个对象最基本的行为。由于Object涉及到JVM加载的过程,很多方法都是native方法,可以通过下载OpenJDK源码来查看方法的具体内容。

Method Return Paremeters
registerNatives - -
getClass Class -
hashCode boolean -
equals int Object obj
clone Object -
toString String -
notify - -
notifyAll - -
wait - long timeout, int nanos
finalize - -

registerNatives

在OpenJDK源码中,可以看到有一份文件1里包含着registerNatives()的实现:

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},};JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls){ (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));}/**** * used in RegisterNatives to describe native method name, signature, and function pointer. */typedef struct { char *name; char *signature; void *fnPtr;} JNINativeMethod;

native方法在JNI中根据类路径添加了前缀。比如:java.lang.Object.registerNatives被命名为Java_java_lang_Object_registerNatives。可以看到除了getClass方法外,其余所有Object类的native方法都经由registerNatives进行注册。registerNatives()在java.lang.Object中,经由static块进行调用,确保所有方法在一开始就进行加载。

如果需要在JNI中重新绑定native方法或者注册新的方法,同样可以改写JDK底层的native方法的实现。

getClass

JNIEXPORT jclass JNICALLJava_java_lang_Object_getClass(JNIEnv *env, jobject this){ if (this == NULL) { JNU_ThrowNullPointerException(env, NULL); return 0; } else { return (*env)->GetObjectClass(env, this); }}

hashCode

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those PRovided by java.util.HashMap.2

hashCode()的行为将会影响哈希表(如HashMap)的性能。hashCode()的设计遵循以下原则:

在单次程序执行期内,同一个对象多次调用hashCode(),返回值要保持一致。对于equals()返回相同的对象,hashCode()必须相同。对于equals()返回不同的对象,hashCode()不要求不相等。但若能保证hashCode()的不同,会对哈希表有性能提升。显然,是由于哈希碰撞的原因。java.lang.Object的hashCode()能够对不同的对象返回不同的哈希值。典型实现是以对象的地址来作为哈希值,但是这并非Java语言所规定的实现。

equals

假设xyz为非空(non-null)的对象,该函数返回有以下性质3456:

reflexive7:x.equals(x)应返回true;symmetric8:x.equals(y)有且在y.equals(x)返回true时返回true;transitive9: 若x.equals(y)y.equals(z),则x.equals(z);consistent10: 多次调用x.equals(y),返回结果相同;对于non-null的对象,与null进行比较应该返回false。

以上的原则其实和代数世界中的等号性质完全一致。注意,由于hashCode()的原则是通过equals()来进行限定的,这两个方法在覆写(Override)时常需要一并考量。

clone

clone()返回该对象的拷贝,通常情况下需要保证返回的clone对象与当前对象引用的地址不同,但equals()为true。

clone()方法还遵循许多惯例(convention):

通常看到以下方式实现clone()

public Object clone() { return super.clone();}

若所有父类都遵循以上实现,最终将会调用Object.clone,通过RTTI(run-time type identification)返回调用clone()方法对应的对象的一个实例。Object.clone会将内存进行bitwise的复制。但是仍然存在一个问题就是对于引用也直接进行拷贝,可以参看以下示例,返回结果表明clone对象内部的nested确实是与原对象相同引用。

public class CloneTest implements Cloneable { class Nested { private int val; public Nested(int val) { this.val = val; } } Nested nested = new Nested(1); @Override protected Object clone() { try { return super.clone(); } catch (Exception e) { return null; } } public static void main(String[] args) { CloneTest ct = new CloneTest(); CloneTest clone = (CloneTest) ct.clone(); System.out.println(ct.nested == clone.nested); // print true }}

一个Object.clone底层的实现会对没有实现Clonable接口的对象直接抛出CloneNotSupportedException,所以想要通过Object.clone完成克隆,就需要实现Clonable接口。实际上,该接口内部没有任何内容。

toString

提供对象的文字描述。Object中的toString返回值由类名以及哈希值的十六进制表示共同组成。

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

notify、notifyAll以及wait

notify() wakes up all threads that are waiting on this object’s monitor. A thread waits on an object’s monitor by calling one of the wait methods.11

Java中所有对象内皆可以作为线程间同步的临界区,每个Object内部都含有一个monitor12。例如,synchronized(obj)即获取obj的monitor。而notify()notifyAll()wait()都是通过ObjectMonitor进行线程同步的手段。当然此外,java.lang.Thread也提供了一些join()sleep()等手段来控制线程的状态,在另一篇文章再研究其区别。

这三个函数涉及很多线程同步的内容,待完全弄明白后一一总结。

finalize

Java GC将会调用即将回收的对象的fianlize(),用于完成对象回收前的结束工作。JVM将待执行的finalize()放入名为F-Queue的队列。GC内部的Finalizer线程会以一个较低优先级执行F-Queue中的函数,所以即便一个不可达(not accessible)的对象在执行GC后也不保证会即刻执行finalize()13:

finalize()确保会被调用,但JVM不保证完全执行。finalize()至多仅会被调用一次。这个函数可以将对象本身重新回到可用状态,逃脱GC的回收,如在函数中重新指定对该对象的引用。Finalizer线程不会获取任何用户可见(user-visible)的锁。finalize()抛出的异常将会被忽略,并终止finalization。

根据《深入理解Java虚拟机 JVM高级特性与最佳实践》中的描述,这个函数实际提供了C++的析构函数的类似功能,但是try-final块等也能完成相关的资源回收工作,并且效率高得多。试想C++的heap上的对象多数由程序员自己或者借由智能指针来完成析构,并且严格保证与构造顺序相反,并不会经过像JVM中这样一个低优先级的、不可保证顺序、不能保证完成整个函数执行的调用过程


OpenJdk7 源码:openjdk/jdk/src/share/native/java/lang/Object.c ↩java.lang.Object源码注释 ↩reflexive: 反身的 ↩symmetric: 相称性的,均衡的 ↩transitive: 可传递的 ↩consistent: 一致的 ↩reflexive: 反身的 ↩symmetric: 相称性的,均衡的 ↩transitive: 可传递的 ↩consistent: 一致的 ↩java.lang.Object源码注释 ↩OpenJdk7 源码:openjdk/hotspot/src/share/vm/runtime/synchronizer.cpp ↩《深入理解Java虚拟机 JVM高级特性与最佳实践》 ↩
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表