1. 引言

本文将深入探讨 Java 中静态变量的初始化过程。这个过程由 Java 虚拟机(JVM)在类加载阶段完成,属于 JVM 类初始化机制的核心部分。

如果你在项目中踩过“静态变量未按预期初始化”的坑,或者对 static 块的执行时机感到模糊,那这篇文章能帮你理清底层逻辑。

2. 类初始化流程

JVM 在加载类时,会按以下三个阶段进行处理:

class initialization process

  1. 加载(Loading):通过类加载器将 .class 文件加载进 JVM,生成对应的 Class 对象。
  2. 链接(Linking):包括验证、准备和解析,其中“准备”阶段会为静态变量分配内存并设置默认值(如 int 为 0,引用类型为 null)。
  3. 初始化(Initialization):执行类构造器 <clinit>() 方法,真正执行静态变量赋值和 static 块中的代码。

⚠️ 只有当类被“主动使用”时,才会触发初始化。比如调用其 main() 方法、创建实例、访问静态成员等。

最后,JVM 会调用该类的 main 方法启动程序。

3. 静态变量(Class Variable)的初始化

在 Java 中,静态变量也称为 类变量(class variable),它属于类本身,而非某个实例。所有实例共享同一份静态变量。

✅ 类的初始化过程会负责初始化这些静态变量。
❌ 而实例变量(非静态)则由对象创建时通过构造器初始化。

来看一个例子:

public class StaticVariableDemo {  
    public static int i;
    public static int j = 20;

    public StaticVariableDemo() {}
}

JVM 执行流程如下:

  1. 加载 StaticVariableDemo 类,生成 Class 对象。
  2. 准备阶段:为 ij 分配内存,初始值均为 0int 默认值)。
  3. 初始化阶段:
    • 按代码顺序执行静态初始化:
      • i 保持默认值 0(无显式赋值)。
      • j 被赋值为 20

📌 关键点:静态变量的初始化顺序严格按照代码书写顺序执行。先定义的先初始化。

4. 静态代码块中的变量

静态块(static {})也是初始化的一部分,它会在类初始化时执行,常用于复杂逻辑或资源加载。

示例:

public class StaticVariableDemo {  
    public static int z;

    static {
        z = 30;
    }
    public StaticVariableDemo() {}
}

执行顺序:

  1. 准备阶段:z 被赋予默认值 0
  2. 初始化阶段:
    • 执行静态块,将 z 修改为 30

✅ 静态块的执行优先于构造器,且只执行一次。
⚠️ 多个静态块按出现顺序依次执行。

这个机制非常适合做单例初始化、配置加载等一次性操作。

5. 静态内部类中的变量

静态嵌套类(static nested class)的静态变量也会在类加载时初始化,但注意:外部类加载并不会自动触发静态内部类的初始化

只有当首次主动使用静态内部类时,才会触发其初始化。

示例:

public class StaticVariableDemo {  
    public StaticVariableDemo() {}
    
    static class Nested {
        public static String nestedClassStaticVariable = "test";
    }
}

行为分析:

  • StaticVariableDemo 被加载时,Nested 类并不会立即加载或初始化。
  • 只有当代码首次访问 Nested.nestedClassStaticVariable 或创建 Nested 实例时,JVM 才会:
    1. 加载 Nested
    2. 初始化其静态变量 nestedClassStaticVariable"test"

✅ 这种延迟加载特性常被用于实现延迟单例(如 Effective Java 中的 singleton 范式)。

6. 总结

  • 静态变量的初始化发生在类的 初始化阶段,由 JVM 的 <clinit>() 方法完成。
  • 初始化顺序:按代码顺序,先变量赋值,后静态块执行。
  • 静态内部类的静态变量 不会随外部类初始化而初始化,具有延迟加载特性。
  • 理解这一机制有助于避免初始化顺序错误、NPE 等隐蔽问题。

如需深入了解底层规范,可查阅 Java Language Specification

所有示例代码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-lang-3


原始标题:When Are Static Variables Initialized in Java?