java虚拟机在准备阶段为static变量分配内存并赋默认值,不执行任何java代码;初始化阶段才执行显式赋值和静态块。

Java虚拟机在类加载过程中为static变量赋默认值,这个动作发生在准备阶段(Preparation),而非初始化阶段。它不执行任何Java代码,也不调用静态块或构造器,只做两件事:分配内存、设默认值。理解这一点,是深度剖析的关键起点。
准备阶段:静态变量的内存分配与默认值设定
当JVM完成类的加载(字节码读入、Class对象创建)后,进入链接阶段,其中第一步就是准备。此时:
- JVM为所有被static修饰的类变量(即静态字段)在方法区(HotSpot中为元空间)分配内存空间;
- 仅对这些变量赋予数据类型对应的默认值:int/long/short/byte → 0,float/double → 0.0f/0.0d,char → '\u0000',boolean → false,引用类型 → null;
-
不执行任何赋值语句——例如
static int x = 3;中的= 3被忽略,x 在此阶段仍为 0; - static final 基本类型或字符串字面量(编译期常量)例外:它们的值在编译时已确定,会直接写入常量池,并在准备阶段“复制”到对应内存位置(即跳过默认值,直接赋真值)。
初始化阶段:真正执行赋值逻辑的时机
准备阶段之后,当类首次被主动使用(如 new 实例、调用静态方法、访问非final静态字段等),JVM才触发初始化阶段。此时才会:
- 按源码声明顺序,逐个执行静态变量的显式初始化语句(如
static int x = 3;); - 按出现顺序执行所有静态代码块(
static { ... }); - 若存在依赖关系(如
static int a = b + 1; static int b = 5;),JVM保证按文本顺序初始化,因此a实际得到的是0 + 1(b尚未赋值),而非6——这正印证了准备阶段默认值已生效、而初始化尚未开始。
区分static变量与实例变量的初始化边界
必须明确:准备阶段只处理static变量,实例变量(非static字段)完全不参与此过程:
- 实例变量的内存分配和默认值设定,发生在对象实例化时(即执行 new 指令、分配堆内存后),由对象创建流程统一完成;
- static变量属于类级别,生命周期与Class对象绑定;实例变量属于对象级别,每次new都独立分配;
- 因此,即使一个类从未被初始化,只要其Class对象已被加载(如通过ClassLoader.loadClass),static变量已在准备阶段获得默认值;而实例变量此时根本不存在。
验证逻辑的典型手段:字节码与运行时观察
要实证上述机制,可结合以下方式交叉验证:
- 反编译.class文件(如用
javap -c),查看是否生成<clinit></clinit>方法——该方法只包含初始化阶段的赋值与静态块,不含准备阶段逻辑; - 在静态变量声明处加断点并配合调试器,观察其值变化:首次进入类上下文前为默认值,进入
<clinit></clinit>后才更新为显式值; - 使用JVM参数
-XX:+TraceClassLoading或-verbose:class观察类加载各阶段触发时机; - 对比
static int x = getValue();与static final int X = getValue();行为差异:前者在初始化阶段调用方法,后者若getValue()非常量表达式,则X也延迟至初始化阶段赋值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











