静态代码块在类初始化阶段执行且仅一次,由首次主动使用触发;按源码顺序执行,父类先于子类;异常未捕获会导致类永久初始化失败。

Java 中 static 静态代码块是类加载过程中执行一次性初始化逻辑的核心机制,它不依赖对象创建,在类首次被主动使用、JVM 进入「初始化阶段」时自动运行,且仅执行一次。
静态代码块的触发时机
它不是在“类加载”(Loading)阶段执行,而是在后续的「初始化」(Initialization)阶段触发。以下动作会促使 JVM 对类进行初始化,从而执行静态代码块:
- 用 new 创建该类的第一个实例
- 访问该类的非编译期常量的静态字段(如
static int PORT = 8080;;但static final String ENV = "prod";不会触发) - 调用该类的静态方法
- 反射调用
Class.forName("ClassName")(默认initialize = true) - JVM 启动时直接运行含
main方法的主类
注意:ClassName.class、ClassLoader.loadClass() 或引用 static final 编译期常量,均不会触发初始化,静态代码块也不会运行。
执行顺序与声明依赖
静态代码块和静态变量赋值语句共同构成 JVM 的 <clinit> 方法,执行严格按源码自上而下顺序进行:
立即学习“Java免费学习笔记(深入)”;
- 父类的静态成员(变量 + 块)总是先于子类执行
- 同一类中,谁写在前面,谁先执行——哪怕中间夹着字段声明
- 若块中读取尚未声明的静态字段,值为默认值(
null、0、false),而非编译错误 - 不能在块中使用
this、实例字段或new当前类实例(易引发循环依赖)
典型初始化场景与正确写法
静态代码块适合处理那些无法用单行赋值完成、又必须在类就绪前准备好的逻辑:
-
加载外部配置:从 properties/YAML 文件读取并解析为
private static final Map<String, Object> -
构建不可变集合:用多步操作组装
ImmutableSet或Map.ofEntries() -
注册驱动或本地库:如
DriverManager.registerDriver(...)或System.loadLibrary("xxx") -
预热全局资源:初始化共享的
ObjectMapper、线程池、缓存容器等
关键实践:异常必须显式捕获并处理(设默认值或转为 RuntimeException),避免未捕获异常导致类进入“初始化失败”状态,后续所有访问都抛 NoClassDefFoundError。
需避开的常见陷阱
看似简单,但容易埋下隐患:
- 耗时操作阻塞启动:网络请求、大文件读取、复杂计算应避免放在静态块中
- 跨类隐式依赖:块中调用其他类的静态方法,而该类尚未初始化,可能造成死锁或空指针
-
线程安全错觉:JVM 保证静态初始化本身线程安全,但块内新建的
HashMap、ArrayList等仍需手动同步 -
误用 this 或实例成员:静态上下文无实例,编译报错或运行时报
IllegalAccessError


















