静态代码块在类首次主动使用进入初始化阶段时执行且仅一次,触发条件包括new实例、调用静态方法、访问非final静态字段、Class.forName反射加载或JVM启动主类;按源码顺序执行,父类优先,线程安全但需规避异常、阻塞与跨类依赖。

静态代码块不是在“类加载时”立刻执行,而是在类进入初始化阶段时运行,且仅一次。这个阶段发生在类被首次主动使用时,比如创建对象、调用静态方法、访问非 final 静态字段等。它天然线程安全,适合做一次性资源准备,但必须避开阻塞、异常失控和跨类依赖陷阱。
哪些操作真正触发静态代码块执行
只有以下动作会让 JVM 启动类初始化,从而运行 static {}:
- 用 new 创建该类或其子类的实例
- 调用该类的 public static 方法
- 读写该类的 非编译期常量静态字段(如
public static int PORT = 8080;) - 通过 Class.forName("X") 反射加载(注意:
ClassLoader.loadClass()不触发) - JVM 启动时直接运行含
main()的主类
反例:仅声明引用(Config c;)、导入类、或访问 public static final String MSG = "ok"; 这类编译期常量,都不会触发。
资源初始化的正确写法与关键细节
加载配置、构建工具对象、预热缓存等,要兼顾安全性、可读性和健壮性:
立即学习“Java免费学习笔记(深入)”;
- 用
MyClass.class.getClassLoader().getResourceAsStream("config.properties")从 classpath 安全读取,路径不加斜杠 - InputStream 必须判空,再用
try-with-resources+InputStreamReader(..., "UTF-8")防乱码 - 所有 IO 或解析逻辑包在
try块内,失败时统一抛ExceptionInInitializerError并带上下文信息 - 结果赋值给
public static final字段,确保不可变;复杂结构用Map.ofEntries()或Collections.unmodifiableMap() - 多个静态块和静态变量按源码顺序交替执行,后面块中若引用前面未声明的字段,值为
null或默认值
父子类与类加载器带来的执行边界
执行时机和次数受继承关系和类加载机制双重影响:
- 子类初始化时,父类静态部分一定先执行:父类变量 → 父类 static {} → 子类变量 → 子类 static {}
- 同一份字节码被不同类加载器(如 AppClassLoader 和自定义 URLClassLoader)加载,会生成两个 Class 对象,各自独立执行一次 static 块
- 热部署、OSGi、Web 容器多租户场景下,“重复执行”往往源于多个 ClassLoader 共存,可用
MyClass.class.getClassLoader().hashCode()验证 - 若 static 块抛出未捕获异常(包括
ExceptionInInitializerError),该类将永久处于初始化失败状态,后续所有访问都报NoClassDefFoundError
耗时或高风险资源怎么安全处理
数据库连接池、远程配置拉取、百万级缓存构建等,不应放在顶层 static 块中:
- 推荐使用静态内部类 Holder 模式:把重逻辑封装进
private static class Holder,对外暴露public static final Resource INSTANCE = Holder.INSTANCE,首次访问才触发初始化 - Spring 环境下优先用
@PostConstruct或实现InitializingBean,便于测试、注入和生命周期管理 - 若必须放 static 块,需加超时控制(如
Future.get(3, TimeUnit.SECONDS))、降级策略(fallback 配置)、明确日志记录 - 严禁在块中调用尚未初始化的其他类静态方法,尤其跨模块时易引发死锁或
NoClassDefFoundError


















