静态代码块仅在类首次被jvm主动使用并进入初始化阶段时执行一次;触发条件包括new实例、调用静态方法、访问非final静态字段、class.forname反射加载或jvm启动运行main类,不因后续操作重复执行。

静态代码块不是类一加载就运行,也不是每次创建对象都执行,而是在类**首次被 JVM 主动使用、进入初始化阶段时执行一次**。关键在于“主动使用”这个前提——它和类加载(Loading)是两个不同阶段,加载完成不等于初始化发生。
哪些操作真正触发静态代码块
只有以下行为会让 JVM 启动类的初始化流程,从而执行静态代码块:
- 用 new 创建该类的第一个实例
- 调用该类的任意 static 方法
- 访问该类的 非 final 静态字段(如
public static String HOST = "localhost";) - 通过 Class.forName("X") 反射加载(注意:
ClassLoader.loadClass("X")不会触发) - JVM 启动时直接运行含 main() 的主类
反例:仅声明变量、导入类、或访问 public static final int PORT = 8080; 这类编译期常量,都不会触发初始化。
执行顺序必须看清的细节
静态代码块和静态变量赋值语句按源码从上到下交替执行,不是“先全赋值再执行块”:
- 若写
static String A = initA();在前,static { B = initB(); }在后,而initA()内部又用了B,此时B还是 null - 父类静态块一定在子类静态块之前完成,这是 JVM 强制保证的
- 多个静态块之间顺序只由当前类文件里的书写位置决定,跨文件无控制能力
适合放什么,不适合放什么
静态代码块是项目启动期做轻量、确定、一次性初始化的理想位置:
- 用
Class.getResourceAsStream()加载本地配置文件(不依赖 Spring) - 构建不可变静态集合,比如
Map.of("a", 1, "b", 2) - 利用 JVM 类初始化锁实现线程安全单例(比双重检查更简洁)
- 校验必要环境变量或系统属性是否存在
应避免:
- 远程 HTTP 调用、数据库连接等可能阻塞或失败的操作
- 启动新线程并等待其完成
- 访问其他尚未初始化完成的类的静态资源(易引发循环依赖)
调试与部署中容易踩的坑
看似简单,但在真实环境中容易误判:
- 热部署、DevTools、OSGi 等场景下,同一类可能被多个类加载器分别加载,每个都会独立执行一次静态块
- 若静态块中抛出未捕获异常(包括
ExceptionInInitializerError),该类即进入“初始化失败”状态,后续所有访问都直接报NoClassDefFoundError - 子类访问父类的非 final 静态字段,也会触发父类初始化——哪怕你没碰过父类本身
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











