在上一篇博文中,我们对JVM的整体架构进行了概览,包括类加载器子系统、运行时数据区、执行引擎、本地方法接口(JNI)以及本地方法库的作用。本文将深入探讨类加载器子系统中的类加载过程。
类的生命周期如下,其中加载的过程包括了加载、验证、准备、解析、初始化五个阶段
<clinit>
方法)类的半初始化问题通常发生在加载阶段和初始化阶段之间。在准备阶段,静态变量已经被分配内存并初始化为默认值,但是在初始化阶段执行之前,静态变量还没有赋予最终的初始值。
这意味着,在准备阶段之后,如果一个类引用了另一个类的静态变量,而这个静态变量还没有被赋予最终的初始值,就会导致类的半初始化问题。
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
本文介绍了类加载器子系统中的类加载过程,以及常说的类的半初始化问题。类加载是Java程序运行的基础,包括加载、验证、准备、解析和初始化五个阶段。了解这些阶段以及如何避免类的半初始化问题对于开发高效、稳定的Java应用程序至关重要。
在后续的博文中,我们将继续深入类加载器子系统,探讨类加载器的层次结构和双亲委派模型等话题。