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

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

MethodReturnParemeters
registerNatives--
getClassClass-
hashCodeboolean-
equalsintObject obj
cloneObject-
toStringString-
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 JNICALL
Java_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()的设计遵循以下原则:

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

equals

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

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

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

clone

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

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

  1. 通常看到以下方式实现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
    
        }
    }
  2. 一个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

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

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


  1. OpenJdk7 源码:openjdk/jdk/src/share/native/java/lang/Object.c
  2. java.lang.Object源码注释
  3. reflexive: 反身的
  4. symmetric: 相称性的,均衡的
  5. transitive: 可传递的
  6. consistent: 一致的
  7. reflexive: 反身的
  8. symmetric: 相称性的,均衡的
  9. transitive: 可传递的
  10. consistent: 一致的
  11. java.lang.Object源码注释
  12. OpenJdk7 源码:openjdk/hotspot/src/share/vm/runtime/synchronizer.cpp
  13. 《深入理解Java虚拟机 JVM高级特性与最佳实践》
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值