首页 > 编程语言 >JDK 源码阅读:java.lang.Object 类

JDK 源码阅读:java.lang.Object 类

时间:2024-04-14 19:56:04浏览次数:34  
标签:lang 调用 java 对象 源码 线程 timeout 方法 wait

记录下自己阅读过程的笔记,如有错误,欢迎指正!

源码参考:https://github.com/kangjianwei/LearningJDK/tree/master

1. 基本介绍

  • 在 Java 中,Object 类是类层次结构的根类
  • 几乎每个 Java 类都直接或间接继承自 Object 类,意味着每个类都继承了 Object 的方法
  • 类结构

2. 源码分析

2.1 静态代码块及本地方法注册

// 本地方法的声明(使用 native 关键字)
private static native void registerNatives();
static {  // 一个静态代码块
    registerNatives();
}
  • 本地方法声明registerNatives()

    • 关键字 native 表示这个方法是通过本地代码(如 C/C++)实现的接口,这些代码直接与底层系统的资源或系统 API 交互

    registerNatives() 方法的主要作用是在内存中注册由本地方法实现的一些关键函数,这些函数通常是性能敏感的,直接与操作系统层交互。这样做可以提高方法调用的效率,避免每次调用时都进行查找和链接过程

  • 静态初始化块

    • 在类被 Java 虚拟机加载时执行,且只执行一次,静态代码块常用于执行类级别的初始化代码
    • 代码块中调用了 registerNatives() 方法来初始化与对象操作相关的本地方法,确保这些方法在被 Java 代码调用前已正确链接

2.2 getClass()

// 返回当前对象所属的类的类对象
public final native Class<?> getClass();
  • 这里的 Class<?> 表示方法返回一个 Class 类型的对象,其中 ? 是一个通配符,表示任何类型。在实际使用中,这个返回类型会更具体地表达为 Class<? extends |X|>,这里的 |X| 表示调用 getClass() 的对象的类型
  • 功能描述getClass() 方法返回一个 Class 类型的对象,该对象在 Java 中代表了调用此方法的对象的实际运行时类,通过这个返回的 Class 对象,可以访问关于类的各种信息,如类的名字、包含的方法、实现的接口

2.3 hashCode()

// 返回当前对象的哈希码
public native int hashCode();
  • 功能描述
  • hashCode() 方法的主要功能是为对象提供一个哈希码值,这个值主要被用于支持哈希表的使用,例如在 Java 集合框架中广泛使用的 HashMap
  • 哈希码是根据对象的内部状态计算出的一个整数,用于确定对象在哈希表中的位置,以实现快速的查找、插入和删除操作
  • 约定和规则
    • 重复调用一致性:对同一个对象多次调用 hashCode() 方法,必须始终返回相同的整数,前提是对象用于 equals 比较的信息没有被修改
    • 等价对象的哈希码相等:为了确保对象能在哈希表中正确存储和检索,规定如果两个对象通过 equals(Object) 方法判断相等,那么这两个对象调用 hashCode() 必须返回相同的整数值
    • 不等对象的哈希码优化:为了提高哈希表的性能,最好为不同的对象生成不同的哈希码,减少哈希碰撞,从而优化数据结构的性能

2.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 中,不同于基本数据类型的比较(使用 ==),对象比较更复杂,因为对象的 “等价性” 可以根据实际需求定义(如属性值相等),equals() 方法提供了一种标准方式来定义两个对象是否等价
    • 在 Java 的集合框架中,例如 HashMapHashSet,对象是否相等直接影响到对象的存储和检索,正确实现 equals() 方法是使用这些集合的前提

2.5 clone()

// 浅拷贝,使用时往往需要重写为 public 形式
// 注意: 要求被克隆的对象所属的类实现 Cloneable 接口
protected native Object clone() throws CloneNotSupportedException;
  • 功能描述

    • clone() 方法的目的是创建一个新的对象,这个新对象在逻辑上与原始对象相等,但在内存中占用不同的位置(即 x.clone() != x 总是为真)
    • 它实现了对象的浅拷贝,即拷贝对象及其非静态字段,但不递归复制字段指向的对象
  • 浅拷贝 VS 深拷贝

    • 浅拷贝:默认的 clone() 方法实现是浅拷贝,意味着对象的非对象字段(如基本数据类型字段)会被完全复制,但对象字段仅复制引用,不复制引用的对象本身
    • 深拷贝:需要手动实现,在 clone() 方法中逐个复制对象内部所有可变的引用类型字段。通常涉及到修改 super.clone() 返回的对象的一个或多个字段,确保所有内部的复杂结构都得到适当的复制
  • 使用限制:Java 要求任何使用 clone() 方法的类必须实现 Cloneable 接口。这个接口是一个标记接口,不包含任何方法,其目的是表明类的设计者已经考虑到了复制问题,并且该类支持进行字段内容的复制

    • 如果一个类没有实现 Cloneable 接口而调用 clone() 方法,将抛出 CloneNotSupportedException异常
  • 特殊情况-数组

    • 所有的数组类型都被视为实现了 Cloneable 接口,因此数组总是可以被克隆,数组类型的 clone() 方法返回的类型也是相同的数组类型 (T[])

2.6 toString()

// 生成对象的字符串表示,往往需要重写
// 默认的实现是将类名与对象的哈希码以十六进制形式结合起来
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • toString() 方法提供了一种将对象转换为人类可读的字符串形式的方式,对于调试和日志记录非常有用,建议所有子类重写此方法

2.7 notify()

// 随机唤醒某个具有相同锁的对象从 wait 状态进入争锁状态
// 方法必须在同步方法或同步块中被调用,以确保调用它的线程持有当前对象的锁
public final native void notify();
  • notify() 方法的核心功能是在多线程环境中管理线程的等待与唤醒机制。它被用来唤醒一个在此对象上调用 wait() 方法而进入等待状态的线程

  • 随机唤醒:当多个线程在某个对象的锁上调用 wait() 方法后进入等待状态时,notify() 方法可以从这些等待的线程中随机选择一个线程,使其从等待状态中返回,准备继续执行

  • 线程状态变化

    • 被唤醒的线程仍需竞争对象锁。只有当前持有锁的线程释放锁后,被唤醒的线程才有机会获取锁并继续执行
    • 唤醒并不意味着立即执行。被唤醒的线程将重新参与竞争该对象锁,其竞争的条件与其他可能正在等待锁的线程无异
  • 调用条件notify() 方法必须由锁的当前持有者调用,即在对象的同步方法或同步块中使用。如果一个线程试图在未持有对象锁的情况下调用 notify(),会抛出 IllegalMonitorStateException

  • 线程如何成为锁的当前持有者

    • 对象的同步实例方法:锁定调用该方法的对象实例
    public synchronized void synchronizedMethod() {
        // ...
    }
    
    • 同步语句块:可以指定锁定任何对象,包括 this(当前实例)、类对象或任何其他对象
    public void someMethod() {
        synchronized (this) {
            // ...
        }
    }
    
    • 类的同步静态方法:锁定的是类的 Class 对象,而非类的某个实例
    public static synchronized void synchronizedStaticMethod() {
        // ...
    }
    

2.8 notifyAll()

// 唤醒所有具有相同锁的对象从 wait 状态进入争锁状态
// 方法必须在同步方法或同步块中被调用,以确保调用它的线程持有当前对象的锁
public final native void notifyAll();
  • 全面唤醒:相比 notify() 随机唤醒单个线程,notifyAll() 确保所有等待的线程都能得到处理机会
  • 竞争公平:所有被唤醒的线程将与其他可能正在尝试同步此对象的线程一起,公平地竞争获取对象锁
  • 使用条件与规范notifyAll() 方法必须由锁的当前持有者调用。如果在未持有对象锁的情况下调用,将抛出 IllegalMonitorStateException,因为只有锁的持有者才能安全地修改等待条件,并通知所有等待线程

2.9 wait

2.9.1 wait(long timeout)

// 等待 timeout 毫秒之后自动醒来,或者靠唤醒(释放锁)
public final native void wait(long timeout) throws InterruptedException;
  • wait(long timeout) 方法使当前线程暂停并释放它持有的对象锁,进入等待状态,直到发生以下事件之一:
    • 被通知: 如果有其他线程对此对象调用了 notify()notifyAll(),当前线程可能被唤醒
    • 超时: 如果指定的等待时间结束,当前线程会自动唤醒
    • 被中断: 如果当前线程在等待期间被中断,它会抛出 InterruptedException
  • 虚假唤醒:指线程可能在没有任何明确通知的情况下唤醒。为了处理这种情况,等待通常应该放在一个循环中,以确保等待的条件确实得到了满足
  • 代码示例
Thread consumerThread = new Thread(() -> {
    synchronized (sharedResource) {
        while (!dataReady) {
            try {
                System.out.println("消费者正在等待数据准备好...");
                // 如果在5000毫秒内生产者准备数据并调用 notify(), 它将被唤醒; 否则, 它将因超时而唤醒
                sharedResource.wait(5000);  // 等待通知或超时
                if (dataReady) {
                    System.out.println("消费者拿到数据!");
                } else {
                    System.out.println("消费者超时! 无可用数据");
                }
            } catch (InterruptedException e) {
                System.out.println("消费者被中断");
            }
        }
    }
});

2.9.2 wait(long timeout, int nanos)

// 至少等待 timeout 毫秒,nanos 是一个纳秒级的附加时间,用来微调 timeout 参数
public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        // 检查传入的 timeout 参数是否小于0,如果是,则抛出 IllegalArgumentException 异常,因为时间不能是负数
        throw new IllegalArgumentException("timeout value is negative");
    }

    // 检查 nanos 参数是否在0到999999纳秒之间,如果不是,则抛出 IllegalArgumentException 异常,因为纳秒值必须在这个范围内
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
            "nanosecond timeout value out of range");
    }

    // 调整超时时间: 如果纳秒参数大于0,则将毫秒超时时间 timeout 加 1,因为在 Java 中,等待时间的最小单位是毫秒,任何非零的纳秒数都至少会导致等待时间增加1毫秒
    if (nanos > 0) {
        timeout++;
    }

    // 调用重载的 wait(long timeout) 方法,传入调整后的毫秒数
    wait(timeout);
}
  • 功能描述wait(long timeout, int nanos) 方法使得当前线程可以在释放对象锁的同时等待至多 timeout 毫秒加 nanos 纳秒。如果在此期间:
    • 被通知: 如果有其他线程对此对象调用了 notify()notifyAll(),当前线程可能被唤醒
    • 超时: 如果指定的等待时间结束,当前线程会自动唤醒
    • 被中断: 如果当前线程在等待期间被中断,它会抛出 InterruptedException
  • 方法作用:提供了比毫秒更细的时间控制单位,即纳秒,对于需要极高时间精度的应用场景非常重要

2.9.3 wait()

// 永不超时,需要靠唤醒(释放锁)
public final void wait() throws InterruptedException {
    wait(0);
}
  • wait() 方法调用 wait(long timeout) 方法并传递 0 作为参数,表示允许当前线程放弃对象锁并无限期地等待,直到其他线程对此对象调用 notify()notifyAll() 方法唤醒它,或者当前线程被中断

2.10 finalize()

// 对象在被 GC 回收后执行的清理操作, 可能会引发 OOM(内存泄漏或内存溢出),建议使用 Java 9 引入的一个工具类  java.lang.ref.Cleaner 替代
protected void finalize() throws Throwable { }
  • 在 Java 早期,finalize() 是处理对象销毁前资源释放的主要方式,它允许在对象被垃圾收集器回收之前执行清理操作
  • 理论上,finalize() 方法中可以采取措施使对象再次可用(例如,重新赋予引用),但通常不推荐,因为会导致对象回收行为变得不可预测
  • 从 Java 9 开始,finalize() 方法已被标记为过时,因为它可能导致不稳定和效率低下的代码

3. 总结

3.1 方法概览

  • getClass():返回对象的运行时类。提供了动态获取类信息的能力,是 Java 反射机制的基础之一
  • hashCode():返回对象的哈希码,主要用于哈希表(如 HashMap)中。它需要与 equals() 方法保持一致,即相等的对象必须有相同的哈希码
  • equals(Object obj):检测某个对象是否等于当前对象。通常需要在自定义类中重写以实现逻辑上的 “相等”
  • clone():创建并返回此对象的一个副本。默认行为是浅拷贝,但可以被重写实现深拷贝
  • toString():返回对象的字符串表示,通常包含类名和哈希码的无符号十六进制表示。通常被重写以提供更多的实例信息
  • notify()notifyAll():用于唤醒在此对象监视器上等待的单个线程或所有线程。这些方法必须在同步块内调用,且调用者必须是对象锁的当前持有者
  • wait() 方法族(wait(), wait(long timeout), wait(long timeout, int nanos):使当前线程放弃对象锁并进入等待状态,直到被通知(notify/notifyAll)或中断。用于线程间的协调和通信
  • finalize():在垃圾收集器回收对象之前调用,用于清理资源。已被废弃并不推荐使用,因为它不可预测、效率低下且容易出错。建议使用 try-with-resourcesCleaner

3.2 归纳

  • 线程同步与通信wait(), notify(), 和 notifyAll() 是 Java 中实现线程间同步和通信的基本机制。这些方法在使用时需要特别注意同步块的设计,以避免死锁或过早通知等问题
  • 对象克隆clone() 方法提供了对象复制的能力,但需要注意浅拷贝与深拷贝的区别及其对应用的影响
  • 哈希与等价hashCode()equals() 方法定义了对象哈希存储和等价比较的标准,重写这些方法时必须遵循一定的约定,以保证它们的一致性

标签:lang,调用,java,对象,源码,线程,timeout,方法,wait
From: https://www.cnblogs.com/thr-0103/p/18134582

相关文章

  • 如何使用 LangChain 构建基于LLMs的应用——入门指南
    大型语言模型(LLMs)是非常强大的通用推理工具,在各种情况下都非常有用。但是,与构建传统软件不同,使用LLMs存在一些挑战:调用往往是长时间运行的,并且随着可用输出而逐步生成输出。与固定参数的结构化输入(例如JSON)不同,它们采用非结构化和任意的自然语言作为输入。它们能够“理解”......
  • Java调用第三方接口获取数据并存储,思路加代码
    思路:1.根据第三方接口返回的字段来创建实体类,用来接收数据2.建立连接,提供两种方式。来获取数据3.实体类转换并存储方法一:URL建立连接进行接收数据依赖<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifa......
  • 上海携程java高级面试题(一)
    一、JVM加载Class文件的原理机制?在面试java工程师的时候,这道题经常被问到,故需特别注意。Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式......
  • java中的final和Object类
    Java中的final和Object类目录Java中的final和Object类修饰变量修饰方法修饰类(无后类)final可以防止指令重排序,保证线程安全Object类经典面试题修饰变量基本类型(值不能改变)引用类型(引用的地址不能改变)final修饰全局变量必须提前赋值final修饰局部变量可以不在声明的时候赋值,......
  • Java登陆第四十二天——跨域
    浏览器拥有最基本的安全机制,同源策略。 违背同源策略的交互,会被浏览器认为是不安全的交互,所以不解析。什么是域?(源) 协议,域名,端口。什么是跨域? 访问不同的域。 即便协议,域名相同,端口号不一样也会违背同源策略。前后端分离后,前端服务器负责视图,后端服务器负责数据。 不......
  • JavaScript判断图片是否已经加载完毕的方法汇总_javascript技巧
    JavaScript判断图片是否已经加载完毕的方法汇总_javascript技巧 在网上有很多关于判断图片是否已经加载完毕的文章,但是有的浏览器并不适合,下面小编给大家分享一些有关JavaScript判断图片是否已经加载完毕方法汇总,具体内容如下所示:一.onload事件通过监听图片的onload事件,可......
  • java 单例模式
    单例模式(SingletonPattern)是Java中最简单的设计模式之一,这种类型的设计模式属于创建型模式。目的是确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。这样做可以节省系统资源,并且保证某些类在系统中只存在一个实例。主要解决:一个全局使用的类频繁地创建与......
  • java中的包装类
    java中的包装类为什么要有包装类在面向对象中“一切皆对象”,但是基本类型的数据不太符合这一理念,为了统一概念,因此,把基本类型包装成了引用类型的数据基本类型对象的包装类型,除了可以满足基本的类型的基本需求,还附加了其他的方法,例如:类型转化,数据类型之间的操作等等基本类型......
  • [javascript]知识点
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/18031963出自【进步*于辰的博客】目录1、其他知识点链接2、严格模式3、类、对象3.1介绍3.2自定义对象3.2.1创建对象方法3.2.2建立继承关系4、变量5、this10、关于var与......
  • Java集合
    Java集合本文中引入的源码为JDK11什么是Java集合Java集合(Java集合类)是java数据结构的实现。Java集合类是java.util包中重要内容,它允许以各种方式将元素分组,并定义了各种使这些元素更容易操作的方法。Java集合类是Java将一些基本的和使用频率极高的基础类进行封装和增强后再......