准备阶段不执行静态代码块,仅分配类变量内存并设默认值(如0、null);静态代码块在初始化阶段执行,且仅一次,由方法统一处理,触发条件为类首次主动使用。

类准备阶段不会执行静态代码块
不会。准备阶段只负责为类变量(static字段)分配内存并设置默认初始值(如0、null、false),不执行任何Java字节码,包括static {}块或static字段的显式初始化表达式。
静态代码块实际在初始化阶段才执行
真正执行static {}和static final非常量字段赋值的地方是「初始化阶段」——也就是类首次被主动使用时(比如第一次调用new、访问static字段、调用static方法等),JVM才会触发该类的<clinit></clinit>方法(由编译器自动生成,合并所有static块和static变量赋值语句)。
- 准备阶段:仅分配内存 + 设默认值(
int x→0,Object o→null) - 初始化阶段:
<clinit></clinit>被执行,static {}运行,static int y = 42这类赋值才真正发生 - 注意:
static final基本类型或字符串字面量常量(如static final int MAX = 100)会在准备阶段直接赋值,因为它们属于编译期常量,被内联进调用处,不依赖<clinit></clinit>
为什么容易误以为准备阶段会执行静态块
常见混淆点来自IDE调试或日志观察时机不准:
- 你在
static {}里加了System.out.println,看到输出就以为“类一加载就执行”,其实那是初始化阶段启动后才发生的 - 某些工具(如Javassist、ASM)在字节码层面修改
<clinit></clinit>,可能让执行逻辑看起来更早,但JVM规范本身严格区分阶段 - 反射调用
Class.forName("X", true, loader)中第二个参数为true才触发初始化;设为false时,类只加载+链接(含准备),static块完全不跑
验证方式:用java -verbose:class观察阶段边界
写一个带static块和打印的类,用以下命令运行:
java -verbose:class -XX:+TraceClassLoadingPreorder YourClass
你会看到:
-
[Loaded YourClass from ...]→ 加载完成(此时还没准备,更没初始化) - 紧接着出现
[Initializing YourClass]→ 初始化开始,这时static块才执行 - 没有单独的「准备中」日志,因为它不涉及用户代码,纯JVM内部内存操作
真正的分水岭不是“类文件读进来”,而是“JVM决定要让这个类对Java代码可见并可用”——那个决定点,就是初始化触发时刻。准备只是初始化前的一个无声铺垫。


















