【JVM】02. 类加载(一):类加载过程

fangwei -

在上一篇博文中,我们对JVM的整体架构进行了概览,包括类加载器子系统、运行时数据区、执行引擎、本地方法接口(JNI)以及本地方法库的作用。本文将深入探讨类加载器子系统中的类加载过程。

1 类加载过程

类的生命周期如下,其中加载的过程包括了加载、验证、准备、解析、初始化五个阶段

2 类的半初始化问题

类的半初始化问题通常发生在加载阶段和初始化阶段之间。在准备阶段,静态变量已经被分配内存并初始化为默认值,但是在初始化阶段执行之前,静态变量还没有赋予最终的初始值。

这意味着,在准备阶段之后,如果一个类引用了另一个类的静态变量,而这个静态变量还没有被赋予最终的初始值,就会导致类的半初始化问题。

class Apple {  
    static Apple apple = new Apple(10);  
    static double price = 20.00;  
    double totalpay;  
  
    public Apple (double discount) {  
        System.out.println("===="+price);  
        totalpay = price - discount;  
    }  
}  
public class PriceTest01 {  
    public static void main(String[] args) {  
        System.out.println(Apple.apple.totalpay);  
    }  
}

运行结果:
====0.0
-10.0

其中Apple.apple访问了类的静态变量,会触发类的初始化,即加载->连接->初始化。

连接结束后,Apple类的apple属性为null,price为0,开始进行类的初始化,即一开始先给静态属性apple属性赋值。当执行构造函数时,price此时还未初始化完成,其值为默认值 0,这时构造函数的 price 就是0,所以最终打印出来的结果是-10 而不是 10。

只需将静态属性price和apple的位置对调,确保在执行apple构造函数前price已初始化完成即可。

class Apple{  
    static double price = 20.00;  
    static Apple apple = new Apple(10);  
    double totalpay;  
  
    public Apple (double discount) {  
        System.out.println("===="+price);  
        totalpay = price - discount;  
    }  
}  
public class PriceTest01 {  
    public static void main(String[] args) {  
        System.out.println(Apple.apple.totalpay);  
    }  
}

运行结果:
====20.0
10.0

3 总结

本文介绍了类加载器子系统中的类加载过程,以及常说的类的半初始化问题。类加载是Java程序运行的基础,包括加载、验证、准备、解析和初始化五个阶段。了解这些阶段以及如何避免类的半初始化问题对于开发高效、稳定的Java应用程序至关重要。

在后续的博文中,我们将继续深入类加载器子系统,探讨类加载器的层次结构和双亲委派模型等话题。