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

静态代码块只在类首次被 JVM 主动使用、进入初始化阶段时执行一次,不是类一加载就运行,也不是每次创建对象都触发——关键在于“主动使用”这个前提。
哪些操作真正触发静态代码块
只有以下行为会让 JVM 启动类的初始化流程,从而执行静态代码块:
- 用 new 创建该类的第一个实例
- 调用该类的任意 static 方法
- 访问该类的非 final 静态字段(如 public static String HOST = "localhost";)
- 通过 Class.forName("X") 反射加载(注意:ClassLoader.loadClass("X") 不会触发)
- JVM 启动时直接运行含 main() 的主类
反例:仅声明变量、导入类、或访问 public static final int PORT = 8080; 这类编译期常量,都不会触发初始化。
执行顺序必须看清的细节
静态代码块和静态变量赋值语句按源码从上到下交替执行,不是“先全赋值再执行块”:
立即学习“Java免费学习笔记(深入)”;
- 若写 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 静态字段,也会触发父类初始化——哪怕你没碰过父类本身

















