Java 的类加载过程

类的加载过程介绍

  1. 介绍

    • 类的加载指的是将类的 .class 文件中的二进制数据读入到 JVM 内存中,将其放在运行时数据区的 方法区 内,然后在 堆区 创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终是位于堆区中的 Class 对象,Class 对象封装了类在方法区内的数据结构,并且提供了访问方法区内的数据结构的接口。

    • 类的加载过程分为 3 个步骤:加载;连接(验证、准备、解析);初始化,一般情况下 JVM 会连续完成 3 个步骤,有时也会只完成前两步。

    • 如图

      类加载过程.jpg
  2. 类加载器介绍

    • 类加载介绍

    • 系统可能在第一次使用某一个类时,加载该类,但也可能采用 预先加载机制 来加载该类,不管怎样类的加载必须由 类加载器 来完成。通常类加载器是由 JVM 提供。

    • 类的加载必须由类加载器完成,通常情况下类加载器由 JVM 提供,但也可以通过自定义。

      1. JVM 提供的类加载器被称之为 系统类加载器

      2. 开发者还可以通过继承 ClassLoader 接口来创建 自定义类加载器

    • 通过不同的类加载器,可以从不同的 "来源" 加载类的 .class 文件(二进制文件)

      1. 从本地系统中直接读取 .class 文件,大部分的加载方式。

      2. 从 ZIP、JAR 等归档文件中加载 .class 文件,很常见。

      3. 从网络下载 .class 文件数据。

      4. 从专有数据库中读取 .class 文件

      5. 将 Java 的源文件数据,上传到服务器中,进行动态编译产生 .class 文件,并加以执行。

    • 但是不管 .class 文件数据来源何处,加载的结果都是相同的

      1. 将字节码文件数据加载到 JVM 内存中,将其放在运行时数据区的 方法区 内,然后在 堆区 创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终是位于堆区中的 Class 对象,Class 对象封装了类在方法区内的数据结构,并且提供了访问方法区内的数据结构的接口。

加载

  1. 作用

    • 通过一个类的全限定名来获取其定义的字节码(二进制字节流),将字节码文件加载到 JVM 内存中,此过程由类加载器完成(可控)
  2. 特点

    • 加载阶段是可控性最强的阶段,既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。

连接

  1. 验证

    • 校验 .class 文件是否合法,遵循 .class 文件格式 参考地址
  2. 准备

    • 为类变量(static 修饰的变量)在 JVM 方法区中分配内存,并进行 默认初始化

      1. int 默认初始化为 0

      2. 引用默认初始化为 null

      3. 等等

    • 静态常量(static final) ,有所不同,直接在 JVM 方法去中 显示初始化

  3. 解析

    • JVM 将常量池的符号引用。替换为直接(地址)引用
  4. 特点

    • 此时在堆区中已经创建一个 java.lang.Class 对象,指向方法区中的数据

初始化

  1. 作用

    • 主要是对类静态的类变量进行 显示初始化 参考地址

      1. init 对非静态变量解析初始化

      2. clinit 是 java.lang.class 类构造器对静态变量,静态代码块进行初始化

    • 类构造器方法(clinit)由编译器收集类中所有类变量的 显示赋值和静态代码块中的语句合并产生

  2. 特点

    • 当初始化某个类时,如果其父类没有初始化,则先触发父类的初始化动作

    • JVM 保证一个类的初始化,在多线中中正确加锁和同步

何时会或者不会触发类初始化动作呢?

  1. 介绍

    • 上面已经介绍,类的加载过程分为 3 步,大部分 3 步按顺序完成,有时也会只完成前两步
  2. 如何区分会不会触发类的初始化

    • 如表

      会触发类的初始化 不会触发类的初始化
      当虚拟机启动时,先初始化 main() 方法所在的类 引用静态常量不会触发此类的初始化
      一次 new 一个类的对象(在 JVM 中一个类的 Class 对象只有一个) 当访问一个静态域时,只有真正声明该域的类才会被初始化(子类继承父类的静态变量,在子类使用该静态变量时,只有父类会初始化,子类不会初始化)
      调用该类的静态变量(static final 除外,因为其在连接时已经显示初始化完成)和静态方法 通过数组定义类引用时,不会触发类初始化(A[] as = new A[2] A 是类,此时不会初始化 A类)
      当初始化某个类时,其父类没有被初始化时,则会先初始化其父类

参考地址

java类的加载机制

java类的加载过程

深入探讨 Java 类加载器

你可能感兴趣的:(Java 的类加载过程)