Java类加载问题

Java 类加载是 Java 虚拟机(JVM)在运行时动态加载 .class 文件到内存的过程。这个过程和类的生命周期紧密相关。我们来系统地学习下 Java 类加载的 过程生命周期阶段类加载器的作用 以及 相关细节和面试点


一、类加载过程(Class Loading Process)

Java 类从 .class 文件变成 JVM 可执行的对象代码,会经过 七个步骤,可以分为三大阶段:

✅ 加载 -> 连接 -> 初始化:

加载(Loading)
 ↓
验证(Verification)
 ↓
准备(Preparation)
 ↓
解析(Resolution)
 ↓
初始化(Initialization)
 ↓
使用(Using)
 ↓
卸载(Unloading)

二、七大生命周期阶段详解:

1. 加载(Loading)

  • 干什么: 找到 .class 文件并读取其字节流。
  • 由谁干: 类加载器(ClassLoader)。
  • 从哪来: 本地磁盘、网络、JAR 包等。
  • 结果: 生成 java.lang.Class 类的实例。

2. 验证(Verification)

  • 干什么: 验证字节码是否合法、符合 JVM 规范。
  • 为什么要: 防止恶意或错误的类破坏 JVM 安全。
  • 检查什么: 魔数、常量池、方法结构、控制流。

3. 准备(Preparation)

  • 干什么: 为类的静态变量分配内存,并设置默认初始值(不会执行初始化赋值)。
  • static int a = 5;
    // 在这阶段,a 分配了内存,值为 0,不是 5
    

4. 解析(Resolution)

  • 干什么: 将常量池中的符号引用(Symbolic Reference)替换成直接引用(Direct Reference)。
  • 如: 把类名 "java/lang/Object" 转成真实的类对象。
  • ⚠️:可能在运行时延迟解析(lazy resolve)。

5. 初始化(Initialization)

  • 干什么: 执行类中的 () 方法(静态代码块和静态变量初始化)。
  • 初始化顺序:
    1. 父类先初始化;
    2. 子类再初始化;
    3. 静态变量和静态代码块按出现顺序执行。

6. 使用(Using)

  • 实例化对象、调用方法、访问字段 都属于使用阶段。

7. 卸载(Unloading)

  • 何时: 某个类对应的 ClassLoader 被 GC 回收,并且该类无任何实例。
  • 注意: JVM 很少卸载类,通常用于动态模块或热部署。

三、类加载器(ClassLoader)

Java 使用双亲委派模型

BootstrapClassLoader(引导类加载器)
   ↑
ExtClassLoader(扩展类加载器)
   ↑
AppClassLoader(应用类加载器)
   ↑
自定义ClassLoader(如 SPI、插件等)

加载器加载顺序(查找顺序):

  1. 请求父加载器先加载;
  2. 如果父加载器找不到,再由当前类加载器尝试加载;
  3. 保证核心类不会被覆盖(比如你写一个 java.lang.String 也不会被加载)。

四、常见类加载时机(何时触发加载)

  1. new 对象时
  2. 访问静态字段或方法
  3. 使用 Class.forName()
  4. 子类初始化时会先初始化父类
  5. 反射调用类成员时
  6. 启动主类时

❗ 五、面试重点&易错点:

内容
类何时初始化? 有静态变量/静态代码块时且被访问
类何时不会初始化? 引用数组不会触发类初始化
() 和构造器区别? () 是类初始化,构造器是对象初始化
自定义 ClassLoader 作用? 动态加载模块、热更新、插件系统
双亲委派有什么好处? 安全、避免重复加载

小练习题:

class A {
    static {
        System.out.println("A static block");
    }
}
public class Test {
    public static void main(String[] args) {
        A[] arr = new A[10]; // 会不会输出 A static block?
    }
}

✅ 不会,因为只是创建了数组,没有初始化类 A


如果你想我配张图解释类加载的阶段,或者深入讲讲某个阶段的底层原理(比如常量池解析),我也可以继续展开。要不要?

你可能感兴趣的:(java,java,开发语言)