目录
- Java类加载机制概述
- 1、加载(Loading)
- 2、链接(Linking)
- 3、初始化(Initialization)
- 静态变量与非静态变量的赋值与初始化过程
- 1、静态变量
- 2、实例变量
- 3、静态初始化块
- 4、总结
Java作为一门成熟的面向对象编程语言,在底层架构中融入了一个复杂而精妙的类加载机制。类加载器(ClassLoader)是Java虚拟机(JVM)的一部分,负责动态加载类文件到内存中。Java中类加载器从高到低主要分三种启动类加载器、扩展类加载器、应用程序类加载器
Java类加载机制概述
Java类加载主要分为三个阶段:加载(Loading)、链接(Linking)、初始化(Initialization)。
1、加载(Loading)
在加载阶段,类加载器负责读取.class文件,将二进制数据转换为方法区中的运行时数据结构,并在堆中生成一个java.lang.Class对象,作为方法区这些数据的访问入口。
2、链接(Linking)
链接阶段又分为验证(Verification)、准备(Preparation)和解析(Resolution)三个部分。
- 验证:确保被加载类的正确性,以确保它满足Java虚拟机规范的要求。
- 准备:为类的静态变量分配内存,并将其初始化为默认值(以及静态常量)。
- 解析:将类中的
符号引用
转换为直接引用
。比如:将方法的名称和描述符转换为指向方法在内存中的具体地址
的直接引用
3、初始化(Initialization)
初始化阶段是类加载的最后一个阶段,主要是对类中静态变量的初始化和执行静态代码块
。在初始化阶段,虚拟机会完成以下工作:
- 执行类构造器(方法),该方法是由编译器自动收集类中所有静态变量的赋值动作和静态代码块中的语句合并产生的。
- 如果类中存在多个静态代码块或静态变量初始化语句,虚拟机会按照其在源代码中的顺序依次执行。
静态变量与非静态变量的赋值与初始化过程
public class MyClass {
static int number;
static String stringConstant = "Hello, World";
static final int finalNumber = 42;
Long num = 12L;
Object o;
final Integer in;
static {
number = 100;
}
}
1、静态变量
static int number;
- 准备阶段:分配内存空间在方法区(JDK 8之前称为永久代,JDK 8及以后为元空间),设置初始值为0。
- 初始化阶段:在静态代码块中被赋值为100。
static String stringConstant = “Hello, World”;
- 准备阶段:方法区分配内存并设置stringConstant初始值为null。
- 初始化阶段:在字符串常量池中创建字符串"Hello, World",并让stringConstant指向它。
static final int finalNumber = 42;
- 准备阶段:因为是 常量(final修饰的静态字段),finalNumber在方法区分配内存空间,并且直接设置为42,不需要等待初始化阶段。
2、实例变量
Long num = 12L;
- 实例化阶段:当创建一个MyClass实例(new MyClass())时,将为num分配内存空间在堆上,并且创建一个Long对象,值为12。
Object o;
- 实例化阶段:当创建MyClass实例时,在堆上分配内存空间,但初始值为null。如果后续有赋值操作,则会指向具体的对象。
final Integer in;
- 实例化阶段:和Object o一样,在实例化时在堆空间分配内存但初始值为null。需要注意的是,因为它是final的,它必须在构造函数中或者在声明时被初始化,否则会导致编译错误。
3、静态初始化块
static {…}
- 初始化阶段:在类的初始化阶段执行,即在类被首次主动使用时。
4、总结
静态变量的内存空间分配和初值设置在类加载的准备阶段完成,除了final修饰的静态基本数据类型和字符串直接量,它们被赋予了程序中给出的值。静态变量的具体赋值操作在初始化阶段完成(如静态初始化块和静态变量赋值语句)。实例变量的内存分配在对象实例化时在堆上完成,实例变量的赋值随着对象的构造过程完成。