Java从JDK源码角度对Object进行实例分析

简介: Java从JDK源码角度对Object进行实例分析

这篇文章主要介绍了Java从JDK源码角度对Object进行实例分析,具有一定借鉴价值,需要的朋友可以参考下。

Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类。比如你随便创建一个classA,虽然没有明说,但默认是extendsObject的。

后面的三个点"..."表示可以接受若干不确定数量的参数。老的写法是Objectargs[]这样,但新版本的java中推荐使用...来表示。例如

publicvoidgetSomething(String...strings)(){}

object是java中所有类的父类,也就是说所有的类,不管是自己创建的类还是系统中的类都继承自object类,也就是说所有的类在任何场合都可以代替object类,根据里氏替换原则,子类在任何场合都可以代替其父类,而父类却不一定能代替其子类,java中常说的万物皆对象说的其实就是这个道理!object类体现了oop思想中的多态,继承,封装,抽象四大特性!

object类是所有类的基类,不是数据类型。这个你可以查询jdk文档了解,所有类都继承自Object。

Object...objects这种参数定义是在不确定方法参数的情况下的一种多态表现形式。即这个方法可以传递多个参数,这个参数的个数是不确定的。这样你在方法体中需要相应的做些处理。因为Object是基类,所以使用Object...objects这样的参数形式,允许一切继承自Object的对象作为参数。这种方法在实际中应该还是比较少用的。

Object[]obj这样的形式,就是一个Object数组构成的参数形式。说明这个方法的参数是固定的,是一个Object数组,至于这个数组中存储的元素,可以是继承自Object的所有类的对象。

这些基础东西建议你多看几遍"Thinkinjava"

Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类。它包含了对象常用的一些方法,比如getClass、hashCode、equals、clone、toString、notify、wait等常用方法。所以其他类继承了Object后就可以不用重复实现这些方法。这些方法大多数是native方法,下面具体分析。

主要的代码如下:

public class Object {
    private static native void registerNatives();
    static {
        registerNatives();
    }
    public final native Class<?> getClass();
    public native int hashCode();
    public Boolean equals(Object obj) {
        return (this == obj);
    }
    protected native Object clone() throws CloneNotSupportedException;
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    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 {
        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);
    }
    public final void wait() throws InterruptedException {
        wait(0);
    }
    protected void finalize() throws Throwable {
    }
}

registerNatives方法

由于registerNatives方法被static块修饰,所以在加载Object类时就会执行该方法,对应的本地方法为Java_java_lang_Object_registerNatives,如下:

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

可以看到它间接调用了JNINativeInterface_结构体的方法,简单可以看成是这样:它干的事大概就是将Java层的方法名和本地函数对应起来,方便执行引擎在执行字节码时根据这些对应关系表来调用C/C++函数,如下面,将这些方法进行注册,执行引擎执行到hashCode方法时就可以通过关系表来查找到JVM的JVM_IHashCode函数,其中()I还可以得知Java层上的类型应该转为int类型。这个映射其实就可以看成将字符串映射到函数指针。

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

getClass方法

getClass方法也是个本地方法,对应的本地方法为Java_java_lang_Object_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);
  }
}

所以这里主要就是看GetObjectClass函数了,Java层的Class在C++层与之对应的则是klassOop,所以关于类的元数据和方法信息可以通过它获得。

JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))
 JNIWrapper("GetObjectClass");
 DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj);
 klassOop k = JNIHandles::resolve_non_null(obj)->klass();
 jclass ret =
  (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror());
 DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret);
 return ret;
JNI_END

hashCode方法

由前面registerNatives方法将几个本地方法注册可知,hashCode方法对应的函数为JVM_IHashCode,即

JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
 JVMWrapper("JVM_IHashCode");
 // as implemented in the classic virtual machine; return 0 if object is NULL
 return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END

对于hashcode生成的逻辑由synchronizer.cpp的get_next_hash函数决定,实现比较复杂,根据hashcode的不同值有不同的生成策略,最后使用一个hash掩码处理。

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
    intptr_t value = 0 ;
    if (hashCode == 0) {
        value = os::random() ;
    } else
     if (hashCode == 1) {
        intptr_t addrBits = intptr_t(obj) >> 3 ;
        value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
    } else
     if (hashCode == 2) {
        value = 1 ;
        // for sensitivity testing
    } else
     if (hashCode == 3) {
        value = ++GVars.hcSequence ;
    } else
     if (hashCode == 4) {
        value = intptr_t(obj) ;
    } else {
        unsigned t = Self->_hashStateX ;
        t ^= (t << 11) ;
        Self->_hashStateX = Self->_hashStateY ;
        Self->_hashStateY = Self->_hashStateZ ;
        Self->_hashStateZ = Self->_hashStateW ;
        unsigned v = Self->_hashStateW ;
        v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
        Self->_hashStateW = v ;
        value = v ;
    }
    value &= markOopDesc::hash_mask;
    if (value == 0) value = 0xBAD ;
    assert (value != markOopDesc::no_hash, "invariant") ;
    TEVENT (hashCode: GENERATE) ;
    return value;
}

equals方法

这是一个非本地方法,判断逻辑也十分简单,直接==比较。

clone方法

由本地方法表知道clone方法对应的本地函数为JVM_Clone,clone方法主要实现对象的克隆功能,根据该对象生成一个相同的新对象(我们常见的类的对象的属性如果是原始类型则会克隆值,但如果是对象则会克隆对象的地址)。Java的类要实现克隆则需要实现Cloneable接口,if (!klass->is_cloneable())这里会校验是否有实现该接口。然后判断是否是数组分两种情况分配内存空间,新对象为new_obj,接着对new_obj进行copy及C++层数据结构的设置。最后再转成jobject类型方便转成Java层的Object类型。

JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
 JVMWrapper("JVM_Clone");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
const KlassHandle klass (THREAD, obj->klass());
JvmtiVMObjectAllocEventCollector oam;
if (!klass->is_cloneable()) {
    ResourceMark rm(THREAD);
    THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
}
const int size = obj->size();
oop new_obj = NULL;
if (obj->is_javaArray()) {
    const int length = ((arrayOop)obj())->length();
    new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
} else {
    new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
}
Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,
                (size_t)align_object_size(size) / HeapWordsPerlong);
new_obj->init_mark();
BarrierSet* bs = Universe::heap()->barrier_set();
assert(bs->has_write_region_opt(), "Barrier set does not have write_region");
bs->write_region(MemRegion((HeapWord*)new_obj, size));
if (klass->has_finalizer()) {
    assert(obj->is_instance(), "should be instanceOop");
    new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);
}
return JNIHandles::make_local(env, oop(new_obj));
JVM_END

toString方法

逻辑是获取class名称加上@再加上十六进制的hashCode。

notify方法

此方法用来唤醒线程,final修饰说明不可重写。与之对应的本地方法为JVM_MonitorNotify,ObjectSynchronizer::notify最终会调用ObjectMonitor::notify(TRAPS),这个过程是ObjectSynchronizer会尝试当前线程获取freeObjectMonitor对象,不成功则尝试从全局中获取。

JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
 JVMWrapper("JVM_MonitorNotify");
 Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
 assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object");
 ObjectSynchronizer::notify(obj, CHECK);
JVM_END

ObjectMonitor对象包含一个_WaitSet队列对象,此对象保存着所有处于wait状态的线程,用ObjectWaiter对象表示。notify要做的事是先获取_WaitSet队列锁,再取出_WaitSet队列中第一个ObjectWaiter对象,再根据不同策略处理该对象,比如把它加入到_EntryList队列中。然后再释放_WaitSet队列锁。它并没有释放synchronized对应的锁,所以锁只能等到synchronized同步块结束时才释放。

void ObjectMonitor::notify(TRAPS) {
    CHECK_OWNER();
    if (_WaitSet == NULL) {
        TEVENT (Empty-Notify) ;
        return ;
    }
    DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
    int Policy = Knob_MoveNotifyee ;
    Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
    ObjectWaiter * iterator = DequeueWaiter() ;
    if (iterator != NULL) {
        TEVENT (Notify1 - Transfer) ;
        guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;
        guarantee (iterator->_notified == 0, "invariant") ;
        if (Policy != 4) {
            iterator->TState = ObjectWaiter::TS_ENTER ;
        }
        iterator->_notified = 1 ;
        ObjectWaiter * List = _EntryList ;
        if (List != NULL) {
            assert (List->_prev == NULL, "invariant") ;
            assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
            assert (List != iterator, "invariant") ;
        }
        if (Policy == 0) {
            // prepend to EntryList
            if (List == NULL) {
                iterator->_next = iterator->_prev = NULL ;
                _EntryList = iterator ;
            } else {
                List->_prev = iterator ;
                iterator->_next = List ;
                iterator->_prev = NULL ;
                _EntryList = iterator ;
            }
        } else
           if (Policy == 1) {
            // append to EntryList
            if (List == NULL) {
                iterator->_next = iterator->_prev = NULL ;
                _EntryList = iterator ;
            } else {
                // CONSIDER: finding the tail currently requires a linear-time walk of
                // the EntryList. We can make tail access constant-time by converting to
                // a CDLL instead of using our current DLL.
                ObjectWaiter * Tail ;
                for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
                assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
                Tail->_next = iterator ;
                iterator->_prev = Tail ;
                iterator->_next = NULL ;
            }
        } else
           if (Policy == 2) {
            // prepend to cxq
            // prepend to cxq
            if (List == NULL) {
                iterator->_next = iterator->_prev = NULL ;
                _EntryList = iterator ;
            } else {
                iterator->TState = ObjectWaiter::TS_CXQ ;
                for (;;) {
                    ObjectWaiter * Front = _cxq ;
                    iterator->_next = Front ;
                    if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
                        break ;
                    }
                }
            }
        } else
           if (Policy == 3) {
            // append to cxq
            iterator->TState = ObjectWaiter::TS_CXQ ;
            for (;;) {
                ObjectWaiter * Tail ;
                Tail = _cxq ;
                if (Tail == NULL) {
                    iterator->_next = NULL ;
                    if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
                        break ;
                    }
                } else {
                    while (Tail->_next != NULL) Tail = Tail->_next ;
                    Tail->_next = iterator ;
                    iterator->_prev = Tail ;
                    iterator->_next = NULL ;
                    break ;
                }
            }
        } else {
            ParkEvent * ev = iterator->_event ;
            iterator->TState = ObjectWaiter::TS_RUN ;
            OrderAccess::fence() ;
            ev->unpark() ;
        }
        if (Policy < 4) {
            iterator->wait_reenter_begin(this);
        }
        // _WaitSetLock protects the wait queue, not the EntryList. We could
        // move the add-to-EntryList operation, above, outside the critical section
        // protected by _WaitSetLock. In practice that's not useful. With the
        // exception of wait() timeouts and interrupts the monitor owner
        // is the only thread that grabs _WaitSetLock. There's almost no contention
        // on _WaitSetLock so it's not profitable to reduce the length of the
        // critical section.
    }
    Thread::SpinRelease (&_WaitSetLock) ;
    if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
        ObjectMonitor::_sync_Notifications->inc() ;
    }
}

notifyAll方法

与notify方法类似,只是在取_WaitSet队列时不是取第一个而是取所有。

wait方法

wait方法是让线程等待,它对应的本地方法是JVM_MonitorWait,间接调用了ObjectSynchronizer::wait,与notify对应,它也是对应调用ObjectMonitor对象的wait方法。该方法较长,这里不贴出来了,大概就是创建一个ObjectWaiter对象,接着获取_WaitSet队列锁将ObjectWaiter对象添加到该队列中,再释放队列锁。另外,它还会释放synchronized对应的锁,所以锁没有等到synchronized同步块结束时才释放。

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
 JVMWrapper("JVM_MonitorWait");
 Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
 assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object");
 JavaThreadInObjectWaitState jtiows(thread, ms != 0);
 if (JvmtiExport::should_post_monitor_wait()) {
  JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
 }
 ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END

finalize方法

这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法

总结

以上就是本文关于Java从JDK源码角度对Object进行实例分析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对"java开发全栈"的支持!

相关文章
|
2月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
59 2
|
8天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
91 53
|
10天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
50 4
|
1月前
|
缓存 Java Maven
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
如何解决Java项目中因JDK版本不匹配导致的编译错误,包括修改`pom.xml`文件、调整项目结构、设置Maven和JDK版本,以及清理缓存和重启IDEA。
45 1
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
|
21天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
17 0
[Java]静态代理与动态代理(基于JDK1.8)
|
1月前
|
Java
Java Object 类详解
在 Java 中,`Object` 类是所有类的根类,每个 Java 类都直接或间接继承自 `Object`。作为所有类的超类,`Object` 定义了若干基本方法,如 `equals`、`hashCode`、`toString` 等,这些方法在所有对象中均可使用。通过重写这些方法,可以实现基于内容的比较、生成有意义的字符串表示以及确保哈希码的一致性。此外,`Object` 还提供了 `clone`、`getClass`、`notify`、`notifyAll` 和 `wait` 等方法,支持对象克隆、反射机制及线程同步。理解和重写这些方法有助于提升 Java 代码的可读性和可维护性。
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
25 1
|
1月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
23 1
|
1月前
|
Python
通过 type 和 object 之间的关联,进一步分析类型对象
通过 type 和 object 之间的关联,进一步分析类型对象
54 3
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决