静态代码块在类首次主动使用时执行一次,用于全局一次性初始化,按源码顺序执行且父类优先,适用于配置加载等场景,但需避免异常、阻塞和隐式依赖。

静态代码块是 Java 中实现程序级初始化策略的核心机制之一,它在类首次被主动使用时执行一次,适合做全局、一次性、与实例无关的准备工作。
执行时机:只在类首次主动使用时触发
静态代码块不是随类加载就立刻运行,而是在 JVM 进入“初始化阶段”时才执行。这个阶段仅在以下任一情况发生时被触发:
- 创建该类的实例(new Child())
- 调用该类的静态方法(Child.doWork())
- 访问或修改该类的非 final 静态字段(Child.count++)
- 反射获取 Class 对象(Class.forName("Child"))
- 初始化子类(会强制先初始化父类)
注意:访问 static final 基本类型常量(如 public static final int PORT = 8080;)不会触发初始化,因为它的值在编译期就被内联了。
执行顺序:严格按源码声明顺序,父类优先
多个静态代码块和静态变量赋值语句,在字节码中被合并为一个 <clinit></clinit> 方法,执行顺序完全由它们在源文件中出现的先后位置决定:
- 父类所有静态成员(变量赋值 + 静态块)从上到下执行完毕
- 再执行子类所有静态成员,同样从上到下
- 同一类中,static { A(); } 写在 static int x = B(); 前面,就先执行 A()
- 若静态块中引用了后面才声明的静态字段,读到的是默认值(如 null、0),不是编译错误
典型初始化策略:配置加载、资源预热、安全校验
静态代码块适用于那些必须在任何实例创建前就准备就绪的全局资源,常见做法包括:
- 读取 properties 或 YAML 配置,解析后存入 private static final Map
- 构建不可变集合:用 ImmutableList.copyOf(...) 或 Set.of(...) 封装多步结果
- 注册 JDBC 驱动:Class.forName("com.mysql.cj.jdbc.Driver")
- 加载本地库:System.loadLibrary("mylib")
- 向监控系统注册类级指标,并添加 JVM 关闭钩子清理资源
关键注意事项:避免失败、阻塞与隐式依赖
看似简单的静态块,一旦出错会导致整个类不可用(抛 NoClassDefFoundError),所以必须谨慎设计:
- 异常必须处理:I/O 或反射操作要加 try-catch,失败时设合理默认值或包装为 RuntimeException
- 禁止耗时操作:网络请求、大文件读取会卡住类加载,拖慢应用启动
- 慎用跨类静态调用:调用尚未初始化的其他类静态方法,可能引发死锁或 NoClassDefFoundError
- 不启动线程、不注册监听器:这些行为依赖运行时环境是否就绪,类加载阶段未必满足
- JVM 保证初始化过程线程安全,但块内新建的 HashMap 等仍需手动同步
静态代码块不是万能方案。对开销大、依赖运行时参数或非必需的初始化,更适合用静态内部类单例(Initialization-on-demand holder)、Spring 的 @PostConstruct,或显式 init() 方法按需触发。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











