Java类加载机制

Java从源代码到运行经历了编译和运行的过程,编译就是将源代码编译成字节码的过程,.java 文件被编译成了 .class 文件。运行过程中,.class 文件被 Java 虚拟机解释,经历了类的加载和类的执行两个过程。以下主要阐述 Java 类的加载

类加载机制

什么是类加载机制

在编译阶段 .java 文件被编译成了 .class 文件,.class 文件中主要是描述类的数据。类的加载就是 Java 虚拟机把描述类的数据从 .class 文件加载到内存,并且队数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型。

Java 类加载机制的过程

Java 类加载机制主要分为类的加载、类的连接(验证、准备、解析)、类的初始化三大过程。

  1. 类的加载

    类的加载主要有三大阶段:定位 .class 文件转换数据存储结构生成 java.lang.Class 对象

    a. 定位 .class 文件

    通过类的全限定名(包名与类名)来获取相应的 .class 文件,可获取 .class 文件的方式有:本地加载、通过网络下载 .class 文件,从 jar 包或者 war包获取、JSP 文件生成等方式

    b. 转换数据存储结构

    这一步完成的是将 .class 文件中的类的静态存储结构转换成运行时在内存中方法区的数据结构

    c. 生成 java.lang.Class对象

    在内存中生成一个 java.lang.Class 对象,这个对象作为方法区中这个类的数据的入口。

  2. 类的初始化

    在类的连接的准备过程中,(仅仅)类的静态常量被初始化赋值(非默认值),在类的初始化阶段,类的静态变量会被初始化赋值(非默认值,在类的连接过程中,静态变量被赋值为默认值)

    以下几种情况需要对类进行初始化:

    ​ a. 使用 new 创建对象

    ​ b. 通过反射调用类的方法和对象时,如果类没有被初始化过的话,就必须对类进行初始化

    ​ c. 初始化子类时,如果这个类的父类没有被初始化的话那么需要先初始化父类

    ​ d. 虚拟机启动时,定义了 main() 方法的类先被初始化

    ​ e. 访问类的静态方法和静态变量(非静态常量)

    常见的被动引用例子:

    ​ a. 通过子类调用父类的静态变量和方法时,只会对父类进行初始化,子类不会被初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // father.java
    public class father {
    static {
    System.out.println("This is father!");
    }
    public static int Fvalue = 1;
    }

    // son.java
    public class son extends father {
    static {
    System.out.println("This is son");
    }
    public static int Svalue = 0;
    }

    // test.java
    public class test {
    public static void main(String[] args) {
    System.out.println(son.Fvalue);
    }
    }

    // result:
    This is father!
    1

    ​ 可以看到通过子类访问父类的静态变量时,父类被初始化了,但是子类没有被初始化

    ​ b. 使用 new 创建类数组

    ​ 稍微修改以上代码:

    1
    2
    3
    4
    5
    6
    // test.java
    public class test {
    public static void main(string[] args) {
    father[] farr = new father[5];
    }
    }

    ​ 从运行的结果可以看出来此时没有进行类的初始化

    ​ c. 访问类的常量时,不会初始化类(常量在类的连接的准备过程中就被初始化了)

    ​ 给 father 类添加一个静态常量:

    1
    2
    3
    4
    5
    6
    7
    8
    // father.java
    public class father {
    static {
    System.out.println("This is father!");
    }
    public static int Fvalue = 1;
    public static final int FSvalue = 11;
    }

    ​ 得到的结果直接输出了 11,可以看到静态常量在类初始化之前就已经被赋值了

Contents
  1. 1. 类加载机制
    1. 1.1. 什么是类加载机制
    2. 1.2. Java 类加载机制的过程
|