roboslyq的技术专栏 JavaEE Coder,micro service

JDK常见源码分析(1)Object


Object

Object对象是Java中所有对象的父类 。一共有12个方法。源码如下(JDK1.8):

package java.lang;

public class Object {
    public Object() {
    }
    //native方法
    private static native void registerNatives();
	//native方法
    public final native Class<?> getClass();
	//native方法
    public native int hashCode();

    public boolean equals(Object var1) {
        return this == var1;
    }
	//native方法
    protected native Object clone() throws CloneNotSupportedException;

    public String toString() {
        return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
    }
	//native方法
    public final native void notify();
	//native方法
    public final native void notifyAll();
	//native方法
    public final native void wait(long var1) throws InterruptedException;
	//wait() 需要被try catch包围,中断也可以使wait等待的线程唤醒。
    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 {
        this.wait(0L);
    }

    protected void finalize() throws Throwable {
    }

    static {
        registerNatives();
    }
}

1. registerNatives

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

registerNatives字面意思为注册本地服务,此方法为本地方法。

从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。

JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。

使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。

2. getClass

获取类的实例的类对象(即Class对象)。同一个Class创建的所有对象实例,getClass()返回的对象相同。

3. hashCode

​ 返回当前对象的哈希值,这个方法主要是为了hashTables相关功能而提供。这个值主要有以下特点:

  • 在一个Java应用中,不管是你调用一次还是多次调用hashCode方法,此方法必须返回同一个hashCode,不能改变。但当在不同的Java应用时(不同的JVM进程),可以不相同。
  • 如果两个对象调用equals方在一个Java应用中,不管是你调用一次还是多次调用hashCode方法,此方法必须返回同一个hashCode,不能改变。当在不同的Java应用时(不同的JVM进程),可以不相同。
  • 如果x.equals(y)法返回true,那么这x,y这两个对象hashCode值一定相同。

  • 两个不同对象(equals为false)产生的hash值不一定要求不能相等。可以有冲突相等。然而,开发者应该注意为了提高hash tables的性能,不同的对象产生的hashCode尽量不相等。

  • 为了保证此方法尽可能的合理实现,hashCode通常通过Object对象来实现。最典型的实现就是将对象的内部地址转换为整数返回。 但是这个实现在Java语言中没有做强制要求。

4. equals

引用equals判断对象是否相等,可以扩展,根据实际情况来判断。

默认是判断hash值是否一样。即与 “==”算法一致。

但很多类进行自定义和扩展,因此不一致。

5. clone

克隆对象。具体使用时,需要实现CloneAble接口。此接口仅是一个标识接口,没有具体的方法。

package com.roboslyq.java.lang;

public class CloneTest {
    public static void main(String[] args) {
        ObjectTest objectTest1 = new ObjectTest();
        objectTest1.setColunm1("你好");
        ObjectTest objectTest;

        {
            try {
                objectTest = objectTest1.clone();
                //com.roboslyq.java.lang.ObjectTest@1540e19d
                System.out.println(objectTest1.toString());
                //com.roboslyq.java.lang.ObjectTest@677327b6
                System.out.println(objectTest.toString());
                // 此处打印为false,表明是一个新对象。。
                System.out.println(objectTest1.equals(objectTest));
               // 此处打印为true,表明是浅复制。即对象的引用对象只是复制了引用值,而不是复制具体的对象。
 			    System.out.println(objectTest.getColunm1()
                               			.equals(objectTest.getColunm1()));
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
    }
}

class ObjectTest implements Cloneable{
    private String colunm1;
    private String colunm2;

    public String getColunm1() {
        return colunm1;
    }

    public void setColunm1(String colunm1) {
        this.colunm1 = colunm1;
    }

    public String getColunm2() {
        return colunm2;
    }

    public void setColunm2(String colunm2) {
        this.colunm2 = colunm2;
    }
    public ObjectTest clone() throws CloneNotSupportedException
    {
      return (ObjectTest) super.clone();
    }
}

打印一:com.roboslyq.java.lang.ObjectTest@1540e19d 打印二:com.roboslyq.java.lang.ObjectTest@677327b6 打印三:false

从打印一和二可知,clone之后是两个不同的对象。打印三也印证了这一点。

若要实现深拷贝,那么当前对象的中的所引用的对象,也要实现Cloneable接口。然后在当前对象clone()方法中调用相关对象的clone()方法,然后赋值给当前对象的属性。

这种关系可以具体传递性(十分烦锁)。比如有四个对象A,B,C,D,D是C的一个属性,C是B的一个属性,B是A的一个属性。如:A –> B –>C –>D,那么A.clone要想完全实现深clone,BC\D均需要实现clone接口,并且实现方式与A中深clone B对象实现一样。伪代码如下:

class A{
    public A clone() throws CloneNotSupportedException
    { 
      A newA = (A) super.clone();
      B newB = this.B.clone();
        newA.setB(newB);
      return newA
    }
} 

6. toString

返回一个类的字符串描述。默认格式为”类名称” + “@” + “16进制的hashCode”。

例如:

com.roboslyq.java.lang.ObjectTest@1540e19d

7. notify

并发相关,本地final方法,无法被重写。通知等待当前对象的锁

8. notifyAll

并发相关,本地final方法,无法被重写。通知所有在等待当前对象的锁

notify/notifyAll配合wait方法使用。只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。

也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程

9. wait系列

等待。此等待并不是没有获得锁,而是已经获得锁,然后因为需要的资源可能还没有准备不能继续往下执行。这时可以释放锁,让别的对象使用。

上锁解锁流程?

当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。

只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。

9.1 wait()

无参wait,一直等待。直到被Interrupted

9.2 wait(long var1)

参数var1为超时时间,单位为ms(milliseconds)。

9.3 wait(long timeout, int nanos)

参数timeout为超时时间,单位为ms(milliseconds)。nanos单位为纳秒,值为0-999999

10. finalize

finalize,翻译过来是完成,使结束的意思。在这里意指对象生命结束时进行的清理工作。但要注意,Java不像C一样开发人员完全掌控着内存的分配和回收,而是由JVM完全控制。所以finalize()和System.gc()方法都不一定靠得住 。这只是一个信号 ,具体是否真正进行回收由JVM GC根据内存情况决定 。

使用下面代码可以进行简单的测试。

package com.roboslyq.java.lang;

public class FinalizedTest {
    public static void main(String[] args) {
        new User();
        System.gc();
    }
}
class User{
    @Override
    public void finalize() throws Throwable {
        super.finalize();
        System.out.println("game over");
    }
}

在大部分情况下,控制台会打印出game over这个字符串。

底层C代码

#include <stdio.h>
#include <signal.h>
#include <limits.h>

#include "jni.h"
#include "jni_util.h"
#include "jvm.h"

#include "java_lang_Object.h"

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]));
}

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

版本:openjdk hotspot-8

Object.java对应的C语言源码路径openjdk\jdk\src\share\native\java\lang\Object.c。其中include "jvm.h"在路径openjdk\jdk\src\share\javavm\export下。

# 参考资料

详解java中Object的clone方法


Comments

Content