JDK8源码:java.lang.Object的详细分析

一、总体描述

        在Java中,Object 类是最基本的类,所有其他的类都是直接或间接地继承自 ObjectObject 类定义了一些基本的方法,这些方法可以被所有类使用。可以说Object类为Java中的对象提供了通用的行为和特征。

二、对源码的详细分析

        Object类中没有成员变量,但是有一些基础的、通用的方法。在实际的使用中,其他继承Object的类经常会重写这些方法,来实现它们自己的功能。下面会挨个介绍对Object类中的方法。

1.registerNatives()

源码:

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

        registerNatives()方法是用来将Java类中的本地方法(比如我们自己写代码的时候,在代码中使用了toString()方法)与这些方法对应的本地代码(一般是用c/c++这类语言写的)通过JNI(Java Native Interface)进行绑定的。

        具体讲,就是,当Java虚拟机加载含有本地方法的类时,它会找到 registerNatives() 方法,然后用它注册这些本地方法。这样,Java虚拟机就能够知道如何调用这些本地方法的实现。

        为了更好地理解,我们可以举一个例子。假如有一场音乐会,只有编写好的音乐(java类中的本地方法)是没法演奏的,需要一个指挥家(Java虚拟机)来指挥(绑定)乐手(本地代码)演奏音乐。“音乐经过指挥家的指挥,被乐手有章法地演奏出来”相当于“我们java类中的本地代码经过操作系统绑定到对应的本地代码,才能被操作系统理解”。

        因此,registerNatives()的关键字包括static(静态的)和native(调用本地代码的)。当java虚拟机首次访问一个类的时候,就会执行registerNatives()。这样能让这个java类在被初始化的时候,里面的方法就被正确注册(绑定本地代码)。

2.getClass()

源码:

public final native Class<?> getClass();

        用来返回当前类的信息。final方法不能被子类重写(下面其他带final的方法不再挨个标注)。

一些常见的可以获取的内容:

        类名:可以通过 getName() 方法获取类的完全限定名称。

        简单类名:可以通过 getSimpleName() 方法获取类的简单名称。

        构造器:可以通过 getDeclaredConstructors() 和 getConstructors() 方法获取类中声明的所有构造器。

        方法:可以通过 getDeclaredMethods() 和 getMethods() 方法获取类中声明的所有方法。

        字段:可以通过 getDeclaredFields() 和 getFields() 方法获取类中声明的所有字段。

        超类:可以通过 getSuperclass() 方法获取类的直接超类。

        接口:可以通过 getInterfaces() 方法获取类实现的所有接口。

        包:可以通过 getPackage() 方法获取类所在的包。

        注解:可以通过 getAnnotations() 方法获取类上的注解

举例:

public class MyClass {
    private int data;

    public MyClass(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass obj = new MyClass(10);

        // 使用 getClass() 获取对象的 Class 对象
        Class<?> clazz = obj.getClass();

        // 打印类名
        System.out.println("Class Name: " + clazz.getName());

        // 打印简单类名
        System.out.println("Simple Class Name: " + clazz.getSimpleName());

        // 打印构造器
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("Constructor: " + constructor);
        }

        // 打印方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("Method: " + method);
        }

        // 打印字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("Field: " + field);
        }

        // 打印超类
        Class<?> superClass = clazz.getSuperclass();
        System.out.println("Superclass: " + superClass.getName());

        // 打印接口
        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> inter : interfaces) {
            System.out.println("Interface: " + inter.getName());
        }

        // 打印包
        Package pkg = clazz.getPackage();
        System.out.println("Package: " + pkg.getName());
    }
}

输出结果:

Class Name: MyClass
Simple Class Name: MyClass
Constructor: public MyClass(int)
Method: public int MyClass.getData()
Field: private int MyClass.data
Superclass: class java.lang.Object
Interface: 
Package: 

3.hashCode()

源码:

public native int hashCode();

        用来返回对象的哈希码。默认的,由Java虚拟机提供的hashCode()方法,会返回对象的内存地址的哈希码。但是在实际应用中,这个方法经常被重写,以便于按照自己希望的方法生成哈希码。

举例:

public class MyClass {
    private int data;

    public MyClass(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }

    @Override
    public int hashCode() {
        // 重写 hashCode() 方法,基于 data 字段生成哈希码
        return Integer.hashCode(data);
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass obj1 = new MyClass(10);
        MyClass obj2 = new MyClass(20);

        // 使用 hashCode() 方法获取对象的哈希码
        int hash1 = obj1.hashCode();
        int hash2 = obj2.hashCode();

        System.out.println("Hash code of obj1: " + hash1);
        System.out.println("Hash code of obj2: " + hash2);
    }
}

运行结果:

Hash code of obj1: 10
Hash code of obj2: 20
4.equals(Object obj)

源码:

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

        在默认情况下,equals方法的比较方式是==,也就是比较两个对象的引用。可以重写这个方法来比较别的东西。

5.clone()

源码:

protected native Object clone() throws CloneNotSupportedException;

        这是一个用来复制对象的方法。默认情况下,clone() 方法执行浅拷贝,即复制对象本身,但对于对象中的引用类型成员,只复制引用,不复制引用的对象。

        另外,调用 clone() 方法之前,如果抛出异常CloneNotSupportedException,则说明调用它的类没有实现Cloneable接口

public class MyClass implements Cloneable {
    private int id;
    private String name;
    private Object reference; // 引用类型成员

    public MyClass(int id, String name, Object reference) {
        this.id = id;
        this.name = name;
        this.reference = reference;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Object getReference() {
        return reference;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 检查是否实现了 Cloneable 接口
        if (!this.getClass().isCloneable()) {
            throw new CloneNotSupportedException();
        }

        // 调用 super.clone() 创建浅拷贝
        MyClass cloned = (MyClass) super.clone();

        // 如果需要深拷贝,可以在此处复制引用类型的成员
        // cloned.setReference(this.reference.clone()); // 假设 reference 实现了 Cloneable

        return cloned;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        MyClass original = new MyClass(1, "Example", new Object());
        MyClass cloned = (MyClass) original.clone();

        System.out.println("Original ID: " + original.getId());
        System.out.println("Cloned ID: " + cloned.getId());
        System.out.println("Original Name: " + original.getName());
        System.out.println("Cloned Name: " + cloned.getName());
        System.out.println("Original Reference: " + original.getReference());
        System.out.println("Cloned Reference: " + cloned.getReference());
    }
}

运行结果:

Original ID: 1
Cloned ID: 1
Original Name: Example
Cloned Name: Example
Original Reference: java.lang.Object@hashcode
Cloned Reference: java.lang.Object@hashcode
6. toString()

源码:

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

        返回由对象转化成的字符串。默认情况下,如上述代码,返回返回的是对象的类名加上 @ 符号和十六进制格式的哈希码(表示这个类的内存地址)。该方法经常被重写。

7.notify()和 notifyAll()

源码:

public final native void notify();
public final native void notifyAll();

        用来唤醒在该对象上等待的单个/所有线程。通常和下文提到的wait一起使用。

        使用这些方法的注意事项:

        (1)必须在同步代码块或者同步方法中调用它们

        (2)调用它们之后,当前线程会释放被唤醒对象的锁,并且允许被唤醒的线程进入同步代码块执行

        (3)如果没有任何线程正处于等待状态,调用它们不会产生任何效果

8.3个wait()方法

wait(long timeout)

源码:

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

        让当前线程等待timeout(单位是毫秒)这么长时间。如果等待时间过了,或者线程被notify()或notifyAll()唤醒,线程才会继续执行。

 wait()

源码:

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

        当wait(0)的时候,这个线程等待的时间会被设置为无限长,也就是在notify之前一直等待。

wait(long timeout, int nanos)

源码:

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

        加入了nanos变量(纳秒),但通过观察代码不难发现,它只是判断表示纳秒的变量是否在正常范围内,如果在的话就会通过

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

直接把表示毫秒的变量timeout+1,然后调用wait(long timeout)方法。

        笔者不太理解创建这个方法的原因。它实际上并没有把时间精确到纳秒级,只是单纯地把等待的毫秒+1。有一种说法是,在多次调用wait的时候,用wait(long timeout)能让误差更小,但是没有解释具体原因。更早版本的JDK中,对应位置的代码如下:

     if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {timeout++;}

也就是,当纳秒不足0.5毫秒时不加1,纳秒大于0.5毫秒时再加1。所以还有一种说法就是,这个方法是当初想解决高并发问题挖的坑,后来发现其实不用这样,为了兼容之前的版本,就直接改成了所有情况都+1。

9. finalize()

源码:

protected void finalize() throws Throwable { }

        这个是用来处理垃圾回收的方法。默认的时候为空,就是什么都不做。可以通过重写这个方法来实现自定义的垃圾回收功能。如果短时间内多次调用finalize()会影响程序的性能,因为java的垃圾回收器需要额外处理这些方法。

        替代方案:使用try-with-resources语句。就是在try-catch语句中直接调用资源。在try-catch语句执行完以后,被调用的资源会被自动释放掉,比如:

 try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        当上述代码被执行完以后,会自动关闭BufferReader。

        当然,还可以自己手动关闭,比如调用close(),也有同样的效果。

以上内容仅为笔者在学习过程中,自己对于java中object类的一些理解和思考。欢迎讨论和指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值