静态代码块仅在类初始化阶段执行,触发条件为五种主动使用行为:new实例、调用public static方法、访问非final静态字段、Class.forName反射加载、JVM启动主类;编译期常量访问不触发;执行顺序严格按源码自上而下,且父类优先于子类。

静态代码块本身不触发类加载,而是类初始化阶段的执行产物;它真正“启动”的前提是 JVM 决定对该类执行初始化——这个决定由**首次主动使用**行为触发,不是写上 static {} 就立刻运行。
哪些操作会真正触发静态代码块执行
只有以下五种情况属于“主动使用”,会推动 JVM 完成类的初始化(含静态代码块):
- 用 new 创建该类的第一个实例
- 调用该类的任意 public static 方法
- 访问该类的 非 final 静态字段(如
public static String HOST = "localhost") - 通过 Class.forName("com.example.MyClass") 反射加载(注意:
ClassLoader.loadClass()不会触发) - JVM 启动时直接运行包含 main() 方法的主类
特别注意:public static final int PORT = 8080 这类编译期常量,值被内联到调用处,访问它不会触发所在类初始化,静态代码块也就不会运行。
静态代码块不是“一写就执行”,而是有严格依赖顺序
它和静态变量赋值语句混合书写时,执行顺序完全按源码从上到下交替进行:
立即学习“Java免费学习笔记(深入)”;
-
static String a = getValue();—— 先执行这行,若getValue()中引用了尚未执行的b,得到的是默认值null或0 -
static { b = compute(); }—— 紧接着执行这个块 - 父类的静态内容总在子类之前完成,哪怕子类先被 new
这种顺序直接影响初始化结果。比如配置加载依赖工具类,就得把工具类的静态块或字段声明写在前面。
实战中容易误判的典型场景
你以为类加载了,其实它还没初始化;你以为没加载,其实已被间接触发:
- 子类调用父类的静态方法 → 触发父类初始化(父类静态块执行),但子类不一定初始化
- Spring Boot 启动时扫描组件,可能通过反射调用
Class.forName(),导致大量静态块提前运行 - 热部署、DevTools、OSGi 环境下,同一类被不同 ClassLoader 加载,每个都会独立执行一次静态块
- 静态块里抛出未捕获异常(如
IOException)→ 类进入“初始化失败”状态 → 后续所有访问都抛NoClassDefFoundError
适合用静态代码块做的事
它适合做轻量、确定、无外部阻塞的一次性准备,例如:
- 用
Class.getResourceAsStream()加载 classpath 下的配置文件并解析为Properties - 构建不可变的静态映射表:
Map.of("A", 1, "B", 2)或手动put默认项 - 注册 JDBC 驱动(兼容老版本,JDBC 4.0+ 可省略但可作为兜底)
- 校验关键环境变量:
if (System.getProperty("env") == null) throw new ExceptionInInitializerError()
避免在这里发起 HTTP 请求、连接数据库、等待线程结束或调用尚未初始化的其他类静态资源——容易引发死锁或循环依赖。


















