深度剖析JDK 8中Object:源码级解读与实战探秘

深度剖析JDK 8中Object:源码级解读与实战探秘

一、Object类概述

在Java编程中,Object类是所有类的根父类,无论是否显式声明,所有的Java类都直接或间接地继承自java.lang.Object类。这意味着每个Java对象都继承了Object类的方法,这些方法为所有对象提供了一组基本的行为和特征,保证了Java语言的统一性和一致性。由于Object类是所有类的基类,所以在使用java.lang.Object包下的类时不需要导入包。

(一)Object类的作用

  • 统一规范:为所有Java对象提供了一组基本方法,使得任何Java对象都能使用这些方法,保证了Java语言的统一性和一致性。
  • 多态支持Object类中的方法如equals()hashCode()toString()确保了Java中的所有对象都具备基本的行为特征,这为多态性、封装性和继承性提供了基础。
  • 基础方法:定义了一些基础操作,比如对象比较、克隆、线程调度相关的wait/notify等方法。

(二)Object类的构造方法

Object类没有显示的构造方法,只有编译器默认提供的无参构造方法。如果构造函数内没有任何操作,那么该方法会省略不写,如Object类;如果有特殊处理,则会手动写出来,如ArrayList

(三)Object类的字段

Object类没有定义字段。

二、JDK 8 Object源码分析

(一)类定义

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);
    }
    // 浅拷贝,使用时往往需要重写为 public 形式
    // 注意: 要求被克隆的对象所属的类实现 Cloneable 接口
    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();
    // 使当前线程等待,直到另一个线程为此对象调用notify方法或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);
    }
    // 使当前线程等待,直到另一个线程为此对象调用notify方法或notifyall方法
    public final void wait() throws InterruptedException {
        wait(0);
    }
    // 在对象被垃圾回收器回收之前会调用这个方法(不推荐使用)
    protected void finalize() throws Throwable { }
}

(二)方法详解

1. 静态代码块及本地方法注册
private static native void registerNatives();
static {
    registerNatives();
}
  • 关键字含义:关键字native表示这个方法是通过本地代码(如C/C++)实现的接口,这些代码直接与底层系统的资源或系统API交互。registerNatives()方法的主要作用是在内存中注册由本地方法实现的一些关键函数,这些函数通常是性能敏感的,直接与操作系统层交互。这样做可以提高方法调用的效率,避免每次调用时都进行查找和链接过程。
  • 静态初始化块:在类被Java虚拟机加载时执行,且只执行一次,静态代码块常用于执行类级别的初始化代码。代码块中调用了registerNatives()方法来初始化与对象操作相关的本地方法,确保这些方法在被Java代码调用前已正确链接。
2. getClass()方法
public final native Class<?> getClass();
  • 功能描述:返回当前对象所属的类的类对象。这里的Class<?>表示方法返回一个Class类型的对象,其中?是一个通配符,表示任何类型。在实际使用中,这个返回类型会更具体地表达为Class<? extends |X|> ,这里的|X|表示调用getClass()的对象的类型。通过这个返回的Class对象,可以访问关于类的各种信息,如类的名字、包含的方法、实现的接口等。
  • 示例代码
Number n = 0;
Class<? extends Number> c = n.getClass();

在上述代码中,不需要进行强制类型转换,因为getClass()方法返回的类型已经是具体的类型。

3. hashCode()方法
public native int hashCode();
  • 功能描述hashCode()方法的主要功能是为对象提供一个哈希码值,这个值主要被用于支持哈希表的使用,例如在Java集合框架中广泛使用的HashMap。哈希码是根据对象的内部状态计算出的一个整数,用于确定对象在哈希表中的位置,以实现快速的查找、插入和删除操作。
  • 约定和规则
    • 重复调用一致性:对同一个对象多次调用hashCode()方法,必须始终返回相同的整数,前提是对象用于equals比较的信息没有被修改。
    • 等价对象的哈希码相等:如果两个对象通过equals(Object)方法判断相等,那么这两个对象调用hashCode()必须返回相同的整数值。
    • 不等对象的哈希码优化:为了提高哈希表的性能,最好为不同的对象生成不同的哈希码,减少哈希碰撞,从而优化数据结构的性能。
4. equals(Object obj)方法
public boolean equals(Object obj) {
    return (this == obj);
}
  • 功能描述equals()方法用于确定调用对象与作为参数传递的对象(obj)是否 “相等”。在Object类的实现中,仅当两个对象引用指向同一内存地址时,才认为它们相等。
  • 等价关系规则
    • 自反性:对于任何非空引用值xx.equals(x)必须返回true
    • 对称性:对于任何非空引用值xyx.equals(y)应当仅在y.equals(x)也返回true时返回true
    • 传递性:如果x.equals(y)y.equals(z)都返回true,则x.equals(z)也应返回true
    • 一致性:只要对象xy的等价比较中用到的信息没有改变,反复调用x.equals(y)应当始终返回同样的结果。
    • null的比较:对于任何非空引用值xx.equals(null)应当返回false
  • 实现注意事项:在自定义类中重写equals()方法时,应确保遵守上述等价关系的规则。当重写equals()方法时,也应该重写hashCode()方法 ,以保持hashCode()的一般约定,即相等的对象必须具有相等的哈希码。在Java的集合框架中,例如HashMapHashSet ,对象是否相等直接影响到对象的存储和检索,正确实现equals()方法是使用这些集合的前提。
5. clone()方法
protected native Object clone() throws CloneNotSupportedException;
  • 功能描述clone()方法的目的是创建一个新的对象,这个新对象在逻辑上与原始对象相等,但在内存中占用不同的位置(即x.clone() != x总是为真)。它实现了对象的浅拷贝,即拷贝对象及其非静态字段,但不递归复制字段指向的对象。
  • 浅拷贝VS深拷贝
    • 浅拷贝:默认的clone()方法实现是浅拷贝,意味着对象的非对象字段(如基本数据类型字段)会被完全复制,但对象字段仅复制引用,不复制引用的对象本身。
    • 深拷贝:需要手动实现,在clone()方法中逐个复制对象内部所有可变的引用类型字段。通常涉及到修改super.clone()返回的对象的一个或多个字段,确保所有内部的复杂结构都得到适当的复制。
  • 使用限制:Java要求任何使用clone()方法的类必须实现Cloneable接口。这个接口是一个标记接口,不包含任何方法,其目的是表明类的设计者已经考虑到了复制问题,并且该类支持进行字段内容的复制。如果一个类没有实现Cloneable接口而调用clone()方法,将抛出CloneNotSupportedException异常。所有的数组类型都被视为实现了Cloneable接口,因此数组总是可以被克隆,数组类型的clone()方法返回的类与原数组类型相同。
6. toString()方法
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 功能描述:返回一个表示这个对象的字符串。该字符串由类全名 + @ + 对象的哈希值的无符号十六进制组成。在使用println方法打印对象时会默认调用这个方法。在实际开发中,通常会重写该方法以提供有意义的字符串描述。
7. notify()方法
public final native void notify();
  • 功能描述:激活正在等待此对象监视器的单个线程。如果有线程正在等待这个对象,那么会选择唤醒其中一个线程。选择是任意的,并由实施者自行决定。线程通过调用其中一个wait方法来等待对象的监视器。
  • 调用条件:此方法只能由该对象监视器的所有者线程调用。线程通过以下三种方式之一成为对象监视器的所有者:
    • 通过执行该对象的同步实例方法。
    • 通过执行在对象上同步的synchronized语句体。
    • 对于Class类型的对象,通过执行该类的同步静态方法。
  • 注意事项:在当前线程放弃此对象上的锁之前,唤醒的线程将无法继续。唤醒线程将以通常的方式与任何其他可能在该对象上为同步而主动竞争的线程竞争;例如,唤醒线程在成为下一个锁定该对象的线程时不享有可靠的特权或劣势。
8. notifyAll()方法
public final native void notifyAll();
  • 功能描述:唤醒正在等待此对象监视器的所有线程。线程通过调用其中一个wait方法在对象的监视器上等待。
  • 调用条件和注意事项:与notify()方法相同,此方法只能由该对象监视器的所有者线程调用。在当前线程放弃此对象上的锁之前,唤醒的线程将无法继续。唤醒的线程将以通常的方式与任何其他可能在该对象上为同步而主动竞争的线程竞争。
9. wait(long timeout)方法
public final native void wait(long timeout) throws InterruptedException;
  • 功能描述:使当前线程等待,直到另一个线程为此对象调用notify方法或notifyall方法,或者指定的时间已过。当前线程必须拥有此对象的监视器。
  • 线程状态变化:此方法使当前线程将自身置于此对象的等待集中,然后放弃此对象上的所有同步声明。出于线程调度目的,线程将被禁用,并处于休眠状态,直到发生以下四种情况之一:
    • 其他一些线程调用此对象的notify方法,而线程恰好被任意选择为要唤醒的线程。
    • 其他一些线程为此对象调用notifyall方法。
    • 其它一些线程interrupt方法中断当前线程。
    • 指定的实时时间已过,或多或少。但是,如果超时为零,则不考虑实时性,线程只需等待通知。
  • 假唤醒问题:线程也可以在不被通知、中断或超时的情况下唤醒,这就是所谓的假唤醒。虽然在实践中很少发生这种情况,但是应用程序必须通过测试导致线程被唤醒的条件来防范这种情况,并且如果条件不满足,继续等待。换句话说,等待应该总是以循环的形式出现。
10. 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);
}
  • 功能描述:使当前线程等待指定的毫秒数和纳秒数。该方法内部调用的是wait(long timeout)方法,如果nanos大于0,则将timeout加1。
11. wait()方法
public final void wait() throws InterruptedException {
    wait(0);
}
  • 功能描述:使当前线程等待,直到另一个线程为此对象调用notify方法或notifyall方法。该方法调用的是wait(long timeout)方法,将超时时间设置为0,表示无限等待。
12. finalize()方法
protected void finalize() throws Throwable { }
  • 功能描述:在对象被垃圾回收器回收之前会调用这个方法。但由于其执行时间的不确定性,现在已经不推荐使用该方法来释放资源。该方法在Object类中的实现为空,子类可以重写该方法来实现特定的清理操作。

三、Object类与其他相关类和工具类的比较

(一)Object类与Objects类

java.util.Objects类是一个工具类,提供了一系列静态方法,用于对对象进行操作,避免了空指针异常。例如,Objects.equals()方法可以安全地比较两个对象是否相等,会先判断对象是否为null,而Object类的equals()方法在比较null对象时会抛出NullPointerException。示例代码如下:

import java.util.Objects;

public class MyClass {
    private String name;

    public boolean isNameEqual(String otherName) {
        // 使用Objects类的equals静态方法进行null安全比较
        return Objects.equals(this.name, otherName);
    }
}

(二)Object类与String类

String类重写了Object类的equals()方法,用于比较两个字符串的内容是否相等。它会逐字符地检查两个字符串的值是否相同,对null值的比较将直接抛出NullPointerException。而Object类的equals()方法默认比较两个对象的引用地址是否相等。示例代码如下:

String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // 输出 true,比较内容
System.out.println(s1 == s2); // 输出 false,比较引用地址

四、使用Object类的注意事项

  • 类型转换:如果频繁地使用Object类型的变量来存储不同类型的对象,可能会遇到类型转换的问题。这时,需要小心处理ClassCastException异常。例如:
Object obj = "hello";
Integer num = (Integer) obj; // 会抛出ClassCastException异常
  • 方法重写Object类的一些方法,如equals()hashCode(),如果不重写,可能会导致意外的行为。因此,在使用Object类时,需要对这些细节有充分的了解。例如,在自定义类中,如果需要比较对象的内容是否相等,就需要重写equals()方法,并且为了保证在哈希集合中正常使用,还需要重写hashCode()方法。
  • finalize()方法的使用:由于finalize()方法的执行时间不确定,不推荐使用该方法来释放资源。可以使用try-with-resources语句或finally块来确保资源的正确释放。

综上所述,Object类在Java中扮演着至关重要的角色。它不仅是所有类的根基,还提供了一系列基础方法,使得我们能够更灵活地处理对象。然而,使用Object类时也需要注意一些细节,以避免潜在的问题。在编程过程中,理解并正确使用Object类可以大大提高代码的灵活性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值