java静态代码块只在类初始化阶段执行一次,但受类加载器影响:同一类加载器下仅一次,不同类加载器会各自执行;触发条件包括new实例、调用静态方法、读写非final静态字段等。

Java静态代码块确实只执行一次,但这个“一次”不是笼统的绝对概念,而是严格绑定在类加载器实例和类初始化阶段上的。它不会因创建多个对象、反复调用静态方法而重跑,但若类被不同类加载器重复加载,就会各自执行一遍——这不是 bug,是 JVM 规范行为。
什么时候真正触发 static 块执行
static 块不随类加载(Loading)发生,而是在初始化(Initialization)阶段首次主动使用类时才运行。以下任一操作首次发生,即触发:
- new 一个该类的实例(如
new ConfigLoader()) - 调用该类任意静态方法(如
ConfigLoader.init()) - 读写该类非 final 的静态字段(如
ConfigLoader.timeout) - 通过
Class.forName("ConfigLoader", true, cl)反射加载(initialize=true) - 子类初始化时,父类尚未初始化 → 先执行父类 static 块
注意:仅声明引用(ConfigLoader ref;)、或访问 final static 编译期常量(如 public static final int VERSION = 1;),不会触发初始化,也就不会执行 static 块。
为什么你看到它“执行了多次”
多数所谓“重复执行”,其实源于类加载器未被回收,而非 static 块失效:
-
Spring Boot 热部署/重启:旧的
WebAppClassLoader没释放,新启动又加载同一类 → 两个 classloader 各跑一次 static 块 - OSGi 或模块化容器:不同 bundle 使用独立类加载器,同名类被视为不同类型
- 自定义 ClassLoader 频繁 new 实例:每次 new 加载器都可能重新加载类
验证方式很简单:在 static 块里打印当前类加载器哈希值:System.out.println("Loaded by: " + AppConfig.class.getClassLoader());
如果输出多个不同地址,就说明是多加载器问题,不是 static 块失约。
实战中确保“真·只执行一次”的关键做法
光写 static{} 不够,需配合编码约束:
-
配置字段声明为
public static final:防止后续误修改,也明确表达不可变语义 -
IO 资源必须 try-with-resources + 显式异常处理:抛出
ExceptionInInitializerError会让类初始化失败,之后所有对该类的访问都会报NoClassDefFoundError - 避免在 static 块中调用其他尚未初始化类的静态成员:否则可能引发死锁或循环初始化(如 A.static 块访问 B.field,B.static 块又依赖 A.field)
- 不要试图在 static 块里用 Spring 注解:@Value、@Autowired、Environment 全部为 null —— 此时 ApplicationContext 还没启动
典型安全写法示例:
public class DbConfig {public static final String URL;
public static final int POOL_SIZE;
static {
Properties p = new Properties();
try (InputStream is = DbConfig.class.getResourceAsStream("/db.properties")) {
if (is == null) throw new RuntimeException("db.properties missing");
p.load(is);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
URL = p.getProperty("jdbc.url");
POOL_SIZE = Integer.parseInt(p.getProperty("pool.size", "5"));
}
}
多个 static 块的执行顺序与协作
同一个类里若有多个 static 块,JVM 会按它们在源码中出现的从上到下顺序合并执行,且全部发生在任何静态变量显式赋值语句之后、静态方法可调用之前。
例如:
static { System.out.println("A"); }static int x = 10;
static { System.out.println("B, x=" + x); }
输出一定是:
AB, x=10
这意味着:可把前置依赖逻辑(如资源加载)放最上面,后续字段初始化依赖其结果,自然形成可靠顺序。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











