静态代码块在jvm类初始化阶段执行且仅一次,由jvm规范保障天然线程安全;其执行依赖首次主动使用触发,多个静态块按源码顺序执行,整体构成一次原子初始化过程。

能,但前提是类没被卸载重加载——静态代码块在 JVM 类初始化阶段执行且仅一次,这是由虚拟机规范保障的天然行为,不是靠程序员“控制”出来的。
它为什么只执行一次
静态代码块被编译进类的
- 主动使用包括:调用静态方法、访问非 final 静态字段、new 实例、Class.forName("X", true, cl)、子类初始化时父类未初始化
- 不触发初始化的操作:仅声明引用(Test t;)、访问 static final 编译期常量(如 public static final int PORT = 8080;)
- 多个静态代码块按源码顺序依次执行,整体算作“一次初始化过程”
什么情况下会“看起来执行多次”
不是 static 块失效,而是 JVM 加载了多个“逻辑同名、实际不同”的类——关键在类加载器。
- Spring Boot 热部署重启:旧 WebAppClassLoader 未释放,新实例用新加载器加载同一类,各自执行一次
- OSGi 或模块化环境:不同 bundle 使用独立类加载器,同名类被视为不同类型
- 自定义 ClassLoader 频繁 new:每次新建加载器都可能重新加载类
- 验证方式:在 static 块里打印 getClassLoader().hashCode(),若输出不同值,说明是多加载器问题
怎么确保它真·只执行一次
光写 static {} 不够,需配合编码约束:
- 静态字段尽量声明为 public static final,明确不可变语义,防误改
- 涉及 IO 或资源初始化,必须用 try-with-resources,并捕获异常;否则抛出 ExceptionInInitializerError 后,所有对该类的访问都会失败
- 避免跨类静态依赖死锁:比如 A 的 static 块调 B.field,B 的 static 块又依赖 A.field
- 别在 static 块里用 Spring 注解(@Value、@Autowired),此时 ApplicationContext 还没启动,字段全为 null
它和单例线程安全的关系
静态代码块本身不提供同步能力,但 JVM 类初始化过程是原子、串行、线程安全的——多个线程同时首次调用 getInstance(),只有一个线程执行 static 块,其余阻塞等待。所以用它实现单例,无需 synchronized、volatile 或双重检查锁。
- 适合封装复杂初始化(如读配置、建连接池),失败即终止,避免后续调用处于半初始化状态
- 构造函数简单无依赖时,更推荐直接声明初始化:private static final Singleton INSTANCE = new Singleton();
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











