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

静态代码块在类首次被 JVM 主动使用并进入初始化阶段时执行,且仅一次。它不依赖对象创建,也不受后续实例化影响,是项目启动时做全局、一次性初始化最可靠的位置——但前提是理解它真正“何时”以及“为何”触发。
什么操作会真正触发静态代码块执行
不是所有对类的引用都会触发初始化。只有以下“主动使用”行为才会导致类进入初始化阶段,从而运行静态代码块:
- 用 new 创建该类的第一个实例
- 调用该类的静态方法
- 访问该类的静态字段(但 static final 基本类型常量除外,如 public static final int PORT = 8080;,其值已在编译期内联,不会触发类初始化)
- 通过 Class.forName("X") 反射加载(注意:ClassLoader.loadClass("X") 不会触发初始化)
- JVM 启动时直接运行包含 main() 方法的主类
执行顺序与常见陷阱
静态代码块和静态变量赋值语句按源码从上到下严格交替执行。谁先写,谁先运行——不是“先全赋值再执行块”,也不是“块统一后置”。这直接影响初始化结果:
- static String A = initA(); 和 static { B = initB(); } 混写时,若 initA() 内部访问了尚未执行的 B,得到的是默认值 null 或 0
- 子类初始化前,JVM 会先确保父类已完成初始化,因此父类静态块总在子类静态块之前执行
- 多个静态块之间不能跨文件控制顺序,只认当前类源码顺序
适合初始化哪些内容
静态代码块适合做轻量、确定、无外部强依赖的一次性准备,例如:
- 加载本地配置文件(用 Class.getResourceAsStream(),不依赖 Spring 容器)
- 预填充不可变的静态集合(如 Map.ofEntries() 或手动 put 默认值)
- 构建线程安全的单例实例(利用 JVM 类初始化锁,比双重检查更简洁)
- 校验关键系统属性或环境变量是否存在
避免在其中发起远程调用、等待线程完成、或访问尚未就绪的其他类静态资源——这些容易引发阻塞或循环依赖。
调试与部署注意事项
静态代码块看似简单,但在复杂环境中容易误判:
- 热部署、OSGi、Spring Boot DevTools 场景下,同一类可能被多个类加载器分别加载,每个加载器都会独立执行一次静态块
- 抛出未捕获异常(包括 ExceptionInInitializerError)会导致该类初始化失败,之后任何访问都直接抛 NoClassDefFoundError
- 建议加日志(如 LOGGER.info("AppConfig initialized by {}", getClass().getClassLoader());),明确归属哪个类加载器
- 资源加载务必判空:if (is != null),getResourceAsStream() 找不到资源时返回 null,不抛异常










