Skip to content

类的加载与初始化


类加载过程(Class Loading)

一、是什么?

类加载是JVM将类的字节码文件(.class)加载到内存,并转换为Class对象的过程。它是类生命周期的第一步,包含以下阶段:

二、解决什么问题

  1. 动态加载:运行时按需加载类,避免一次性加载所有类导致内存浪费。
  2. 安全性:通过验证字节码防止恶意代码执行。
  3. 内存管理:为类变量分配内存并设置默认值,确保JVM稳定运行。

三、核心阶段

  1. 加载(Loading)
    • 通过类加载器(ClassLoader)查找字节码文件。
    • 将字节码转换为方法区的运行时数据结构,生成Class对象。
  2. 链接(Linking)
    • 验证(Verification):检查字节码合法性(如格式、语法)。
    • 准备(Preparation):为静态变量分配内存并赋默认值(如int0)。
    • 解析(Resolution):将符号引用(如类/方法名)替换为直接引用(内存地址)。
  3. 初始化前准备:为初始化阶段奠定基础。

四、应用场景

  • 首次创建对象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保证仅执行一次。

二、解决什么问题

  1. 静态资源初始化:确保静态变量和代码块在类使用前正确初始化。
  2. 延迟加载:仅在首次主动使用时触发(如访问静态字段)。

三、触发条件(主动使用)

  1. newgetstaticputstaticinvokestatic指令(如new对象、访问静态字段)。
  2. 反射调用(如Class.forName("MyClass", true, loader))。
  3. 初始化子类时,父类需先初始化。

四、执行顺序

五、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
被动引用可能触发加载绝不触发初始化

总结

  1. 类加载是基础:包含加载、链接(验证/准备/解析),为初始化做准备。
  2. 初始化是执行:运行静态代码和赋值,确保类可用。
  3. 顺序严格:父类先于子类,静态先于实例。
  4. JDK8+优化
    • 字符串常量池移至堆内存,减少永久代(PermGen)溢出。
    • 元空间(Metaspace)替代永久代,降低OutOfMemoryError风险。

关键理解

类加载是“准备食材”,初始化是“开火烹饪”——只有主动使用类时才会“点火”。