JDK源码那些事儿之万物之源Object

从这篇文章开始进行AQS相关的学习,如果你还不明白什么是AQS,可以先去了解下,由于涉及的源码众多,笔者会一步一步进行深入说明整理,在学习AQS前,有很多基础知识是需要我们先去了解的,比如本文所说的Object

前言

JDK版本号:1.8.0_171

我相信很多开发者都没有完全去了解过Object类,只是使用的时候知道而已,其实其中还是有很多可以学习的知识

Java中的父类也称为超类,而Java是面向对象的语言,面向对象的基础就是类。Java中所有类都有一个共同的祖先Object类,Object类是唯一没有父类的类,Object类引用变量可以存储任何类对象的引用。那么作为万物之源的Object类不知道大家有没有去看一看其中的源码呢?

为什么先说Object类呢?因为其中有部分方法涉及到了多线程等待通知机制,也就是线程间通信协作的一种实现方式,是我们深入AQS前应该先了解的知识,方便后期对比,加深理解

Object

接下来我们一起看一看Object的源码实现

registerNatives

之前的源码分析中已经提及到了这个方法的作用,这里就简单再说明下,其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。函数的执行是在静态代码块中执行的,在类首次进行加载的时候执行

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

返回运行时对象的Class类型对象,也就是运行时实际的Class类型对象,同样是native方法,反射也是使用其来实现的

    public final native Class<?> getClass();
hashCode/equals

hashCode返回对象的哈希码,是一个整数,这个整数是对象根据特定算法计算出来的一个散列值(hash值),而equals方法直接使用==进行比对,也就是比较对象的地址,默认的约定是相同的对象必须具有相同的哈希码。子类可以重写hashCode方法,但是重写后euqals方法通常也需要重写,这里就提出了那个经典的问题:为什么hashCode和euqals重写其中一个方法,另一个方法也建议重写?

首先你应该知道的是这两个方法具有以下特性:

  • 两个对象通过equals()判断为true,则这两个对象的hashCode()返回值也必须相同
  • 两个对象的hashCode()返回值相同,equals()比较不一定需要为true,可以是不同对象

为什么要这么定义呢?其实我们联想下HashMap就明白了,HashMap的底层数据结构就是哈希表,通过key的hashCode进行散列,但是这里我们明白还需要解决hash冲突,这里就通过对象key的equals完成对比,我们比较时是需要比较对象的属性值的,而不是对象地址,这也是我们为什么通常使用String对象来作为key,因为String已经对hashCode和equals进行了重写,如果我们自己写了一个对象,没有进行重写,作为HashMap的key,你可以想想会出现什么情况?

    public native int hashCode();

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

clone方法是个本地方法,效率很高。当需要复制一个相同的对象时一般都通过clone来实现,而不是new一个新对象再把原对象的属性值等复制给新对象。Object类中定义的clone方法是protected的,必须在子类中重载这个方法才能使用。clone方法返回的是个Object对象,必须进行强制类型转换才能得到需要的类型。在设计模式中原型模式就是通过clone方法来完成的,有兴趣可以去查找资料进行了解

实现clone方法需要继承Cloneable接口,不继承会抛出CloneNotSupportedException异常

    protected native Object clone() throws CloneNotSupportedException;
toString

可以看到默认的toString实现,这也就是我们未重写时默认的输出,一般而言我们创建对象时都会进行重写,便于日志输出

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

我们可以看到作者的注释:

唤醒正在此对象的monitor上等待的单个线程,如果有任何线程在该对象上等待,则选择其中一个唤醒。该选择是任意的,并且可以根据实现情况进行选择。线程通过调用wait方法在对象的monitor上等待。在当前线程放弃该对象上的锁之前,唤醒的线程将无法继续。唤醒的线程将以通常的方式与任何其他可能正在主动竞争以与此对象进行同步的线程竞争。此方法只能由作为该对象的monitor的所有者的线程调用。线程通过synchronized成为对象monitor的所有者,也就是获取对象的锁,同一时刻只有一个线程可以获得对象的锁

简单说明下,通知可能等待该对象的对象锁的其他线程。由JVM随机挑选一个处于wait状态的线程

  • 在调用notify()之前,线程必须获得该对象的对象级别锁
  • 执行完notify()方法后,不会马上释放锁,要直到退出synchronized代码块,当前线程才会释放锁
  • notify()一次只随机通知一个线程进行唤醒
    public final native void notify();
notifyAll

唤醒正在此对象的monitor上等待的所有线程,在当前线程放弃对该对象的锁定之前,唤醒的线程将无法继续。唤醒的线程将以通常的方式与可能正在竞争在此对象上进行同步的任何其他线程竞争,此方法只能由拥有该对象的monitor的所有者的线程调用

和notify功能差不多,只不过是使所有正在等待线程池中等待同一共享资源的全部线程从等待状态退出,进入可运行状态,让它们竞争对象的锁,只有获得锁的线程才能进入就绪状态

    public final native void notifyAll();
wait

使当前线程等待,直到另一个线程在对象上调用notify方法或notifyAll方法唤醒等待线程,可以等待指定的时间,在等待过程中可以被中断,这里抛出InterruptedException异常

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

注意,在调用此方法前,当前线程必须先拥有对象的锁才能进行,注释上也说明了其使用方法:

   synchronized (obj) {
       while (<condition does not hold>)
           obj.wait(timeout);
       ... // Perform action appropriate to condition
   }
finalize

当垃圾回收确定不再有对该对象的引用时,由垃圾回收器在对象上调用。子类覆盖finalize方法以处置系统资源或执行其他清除。finalize的一般约定是,当Java虚拟机确定不再有任何手段可以使尚未死亡的任何线程访问该对象时(除非由于执行操作而导致),调用finalize。简单点说就是在垃圾回收之前调用,但是不推荐使用,了解下就好

    protected void finalize() throws Throwable { }

synchronized

Object对象中wait/notify/notifyAll就是线程间进行通信协作的一种方式,也就是通常的等待唤醒机制,只是在使用时我们需要先获取对应的对象锁才能进行调用,一般而言通过synchronized完成

那么,synchronized是如何实现的呢?我们通过代码来看看,方法上添加了synchronized和对代码块添加synchronized是不同的

    public synchronized void synchronizedMethod(){
        System.out.println("synchronizedMethod");
    }

    public void synchronizedBlock(){
        synchronized(this){
            System.out.println("synchronizedBlock");
        }
    }

我们反编译下看看,可以看到在方法上的flags上添加了ACC_SYNCHRONIZED标识,而代码块上是多了monitorentermonitorexit两个jvm指令集,那么你应该明白了synchronized是通过JVM底层来实现的,既然不是Java API层面上的实现,那么先了解就好,我们的重点学习部分在于Java API层面上的同步框架实现——AQS

 public synchronized void synchronizedMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String synchronizedMethod
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 14: 0
        line 15: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/example/demo/SynchronizedTest;

  public void synchronizedBlock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #5                  // String synchronizedBlock
         9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
      LineNumberTable:
        line 18: 0
        line 19: 4
        line 20: 12
        line 21: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  this   Lcom/example/demo/SynchronizedTest;

总结

本文简单介绍了万物之源Object的源码和其中的方法,重点关注下wait/notify/notifyAll,其需要结合synchronized来完成线程间通信协作,通过反编译文件理解JVM对其进行的处理,之后会继续进行AQS的相关学习整理

以上内容如有问题欢迎指出,笔者验证后将及时修正,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值