Java编译后的字节码文件,在运行时需要被加载到内存中,才能被正确执行,需要注意类加载过程与类加载时机所描述概念的区别,类加载过程是描述触发类加载后需要进行的步骤,而类加载时机是指什么时候会触发类的加载。
    Java中类从字节码被加载到虚拟机内存中要经历一系列生命周期,包括加载(Loading)、验证(Verification)、
    准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)几个阶段。其中验证、准备、解析统称为连接(Linking)。
    未命名文件 (2).svg
    在Oracle给出的Java虚拟机规范中,并没有强制约束在什么情况下开始类的加载,这个加载机制的触发完全有虚拟机的实现来决定,但是却严格规定了以下几种情况必须对类立即进行初始化(加载、连接过程自然需要在此之前开始):

    • 遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类型没有进行初始化过,需要先触发初始化阶段。这四条指令的Java代码场景有:
      • 使用new关键字创建对象
      • 读取或设置一个类的静态字段(被final修饰的字段除外,编译期已被放入常量池)
      • 调用类的静态方法
    • 使用java.lang.reflect包的方法对类型进行反射调用
    • 初始化类的时候,如果发现父类还未初始化,先触发父类初始化
    • 虚拟机启动阶段,优先初始化用户指定的入口类(如包含main方法的类)
    • 使用JDK7之后动态语言支持时,如果java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且方法句柄对应的类没有初始化时,会先触发类的初始化
    • 使用JDK8之后版本,在接口中定义default方法,接口的初始化要在其实现类初始化之前进行

    这六种场景是Java虚拟机中规定,有且只有的会触发类进行初始化的场景。这些类型所触发点都是对类的主动引用,除此之外,所有引用类型的方式都不会触发初始化。
    被动引用示例,如下代码片段所示:

    1. package com.starsray.skywalking;
    2. /**
    3. * 父类
    4. *
    5. * @author starsray
    6. * @date 2022/04/23
    7. */
    8. class SuperClass {
    9. protected static int value = 1;
    10. static {
    11. System.out.println("SuperClass Init.");
    12. }
    13. }
    14. /**
    15. * 子类
    16. *
    17. * @author starsray
    18. * @date 2022/04/23
    19. */
    20. class SubClass extends SuperClass {
    21. static {
    22. System.out.println("SubClass Init.");
    23. }
    24. }
    25. public class Test {
    26. public static void main(String[] args) {
    27. // 子类调用父类字段
    28. System.out.println(SubClass.value);
    29. }
    30. }

    输出结果

    1. SuperClass Init.
    2. 1

    说明:如果SuperClass中的value字段被final修饰,那么在子类调用的时候,父类也不会进行初始化,输出结果为1,这也与上面描述的六种场景中的第一种相对应。

    在HotSpot VM中可以通过指定启动参数-XX:+TraceClassLoading观察加载的过程。输出结果如下所示

    1. [Loaded java.lang.Void from /opt/develop/jdk1.8.0_301/jre/lib/rt.jar]
    2. [Loaded com.starsray.test.SuperClass from file:/home/starsray/IdeaProjects/java-test/out/production/java-test/]
    3. [Loaded com.starsray.test.SubClass from file:/home/starsray/IdeaProjects/java-test/out/production/java-test/]
    4. SuperClass Init.
    5. 1
    6. [Loaded java.lang.Shutdown from /opt/develop/jdk1.8.0_301/jre/lib/rt.jar]
    7. [Loaded java.lang.Shutdown$Lock from /opt/develop/jdk1.8.0_301/jre/lib/rt.jar]

    类加载中日志显示先加载父类信息,再加载子类信息。