很多人把"类加载"说成一个瞬间:用到了类,JVM 就加载,static 就跑了。
面试官通常不会在这里放过你。他想听的其实是:加载、连接、初始化是不同阶段;真正执行 static 字段赋值和 static 代码块,是初始化阶段。

我觉得最容易讲清楚的方式,不是先背一长串生命周期,而是先拿一段能跑的代码。
java
class Parent {
static {
System.out.println("Parent <clinit>");
}
static int value = 42;
}
class Child extends Parent {
static {
System.out.println("Child <clinit>");
}
}
class Demo {
public static void main(String[] args) {
System.out.println(Child.value);
}
}
输出只有:
xml
Parent <clinit>
42
Child.value 看起来是通过子类读的,但 value 实际声明在 Parent。按 Java 语言规范,只有直接声明这个静态字段的类会因此初始化,所以这里不该出现 Child <clinit>。
这就是这题最常见的坑:不要按"代码里写了哪个类名"判断,要看字段到底定义在哪个类里。
哪些情况会真的触发初始化
先记住几个够用的主动使用场景:
new Config()- 调用类的静态方法,例如
Config.load() - 读取或写入不是编译期常量的静态字段
- 反射主动使用该类
- 启动类的
main执行前
前面几个最终对应到 new、getstatic、putstatic、invokestatic 等使用点。父类需要先于子类完成初始化,所以第一次 new Child() 时,输出顺序会先是父类,再是子类。

反过来,有些写法只是在拿类型信息,别急着说"static 一定执行"。
java
class Config {
static {
System.out.println("Config <clinit>");
}
static final int PORT = 8080;
static final Integer TIMEOUT = 3000;
}
class Demo {
public static void main(String[] args) {
System.out.println(Config.PORT);
System.out.println(Config.TIMEOUT);
System.out.println(Config.class.getName());
}
}
PORT 是编译期常量,调用方编译后可能直接把 8080 写进自己的常量池,不需要初始化 Config。但 TIMEOUT 是 Integer 对象,不属于编译期常量,读取它会触发 Config 初始化。
这块别只盯着有没有 final。判断关键是"是不是编译期常量表达式",不是"字段看上去像个常量"。
Config.class 也不会执行 static 代码块。它拿到的是类字面量,不等于主动初始化。
不是构造方法
继续被追问时,很多人会说"static 代码块在构造函数之前执行"。这句话不算错,但太粗。
编译器会把静态字段的赋值和 static 代码块,按源码出现的顺序收进一个叫 <clinit> 的类初始化方法。它由 JVM 在初始化阶段调用,普通 Java 代码不能直接调。
实例初始化走的是 <init>,也就是构造器相关的那条路径。一个管类,一个管对象。把这两个名字拆开,后面的单例、静态资源初始化、父子类顺序就不容易混。
现场怎么答
我会先用一句话定边界:
"加载、连接和初始化不是一回事。static 真正执行在初始化阶段。new、调用静态方法、读写非常量静态字段会触发它;子类名访问父类字段时,只初始化字段的声明类。"
然后补上 static final int PORT = 8080 和 static final Integer TIMEOUT = 3000 这个对照。面试官一般会接着问父子类顺序,或者让你解释一段输出。