类的加载与初始化
类加载过程(Class Loading)
一、是什么?
类加载是JVM将类的字节码文件(.class
)加载到内存,并转换为Class
对象的过程。它是类生命周期的第一步,包含以下阶段:
二、解决什么问题
- 动态加载:运行时按需加载类,避免一次性加载所有类导致内存浪费。
- 安全性:通过验证字节码防止恶意代码执行。
- 内存管理:为类变量分配内存并设置默认值,确保JVM稳定运行。
三、核心阶段
- 加载(Loading):
- 通过类加载器(
ClassLoader
)查找字节码文件。 - 将字节码转换为方法区的运行时数据结构,生成
Class
对象。
- 通过类加载器(
- 链接(Linking):
- 验证(Verification):检查字节码合法性(如格式、语法)。
- 准备(Preparation):为静态变量分配内存并赋默认值(如
int
为0
)。 - 解析(Resolution):将符号引用(如类/方法名)替换为直接引用(内存地址)。
- 初始化前准备:为初始化阶段奠定基础。
四、应用场景
- 首次创建对象:
new MyClass()
时触发加载。 - 访问静态成员:
MyClass.staticField
。 - 反射调用:
Class.forName("MyClass")
。
五、Java示例
java
public class LoadExample {
static int staticValue = 10; // 准备阶段赋0,初始化阶段赋10
public static void main(String[] args) {
System.out.println(staticValue); // 触发类加载
}
}
六、重要注意事项
- 类加载器层级:
- 启动类加载器(Bootstrap)→ 平台类加载器(Platform)→ 应用类加载器(App)→ 自定义加载器。
- 双亲委派模型:类加载优先委派给父加载器,避免重复加载。
类的初始化过程(Initialization)
一、是什么?
初始化是类加载的最后一步,执行静态变量赋值和静态代码块(static{}
)。它是类生命周期中线程安全的步骤,由JVM保证仅执行一次。
二、解决什么问题
- 静态资源初始化:确保静态变量和代码块在类使用前正确初始化。
- 延迟加载:仅在首次主动使用时触发(如访问静态字段)。
三、触发条件(主动使用)
new
、getstatic
、putstatic
、invokestatic
指令(如new
对象、访问静态字段)。- 反射调用(如
Class.forName("MyClass", true, loader)
)。 - 初始化子类时,父类需先初始化。
四、执行顺序
五、Java示例
java
class Parent {
static { System.out.println("父类静态块"); }
Parent() { System.out.println("父类构造器"); }
}
class Child extends Parent {
static int value = 1;
static { System.out.println("子类静态块, value=" + value); }
Child() { System.out.println("子类构造器"); }
}
public class Test {
public static void main(String[] args) {
new Child(); // 输出:父类静态块 → 子类静态块 → 父类构造器 → 子类构造器
}
}
六、重要注意事项
- 线程安全:JVM通过锁保证多线程下初始化只执行一次。
- 被动引用不触发初始化:java
System.out.println(Child.value); // 触发(主动引用) System.out.println(Child[].class); // 不触发(数组定义)
类加载 vs 初始化:关键区别
特性 | 类加载(Loading) | 初始化(Initialization) |
---|---|---|
阶段 | 包含加载、链接 | 类加载的最后一步 |
核心动作 | 加载字节码、分配内存、验证 | 执行静态块和静态变量赋值 |
触发时机 | 首次主动使用类时 | 主动使用类时(如new ) |
默认值处理 | 准备阶段赋默认值(如int=0 ) | 初始化阶段赋实际值(如int=10 ) |
被动引用 | 可能触发加载 | 绝不触发初始化 |
总结
- 类加载是基础:包含加载、链接(验证/准备/解析),为初始化做准备。
- 初始化是执行:运行静态代码和赋值,确保类可用。
- 顺序严格:父类先于子类,静态先于实例。
- JDK8+优化:
- 字符串常量池移至堆内存,减少永久代(PermGen)溢出。
- 元空间(Metaspace)替代永久代,降低
OutOfMemoryError
风险。
关键理解:
类加载是“准备食材”,初始化是“开火烹饪”——只有主动使用类时才会“点火”。