静态代码块只执行一次,由jvm类初始化机制保障,仅在类首次主动使用时触发初始化阶段;主动使用包括new实例、调用静态方法等,访问static final编译期常量则不会触发。

静态代码块只执行一次,不是因为“写了就只跑一遍”,而是由 JVM 类初始化机制严格保障的——它绑定在类的首次主动使用上,且一旦完成,就被标记为“已初始化”,后续所有调用都跳过。
误区一:以为“类加载时”就立刻执行
很多人误以为 JVM 一加载类(Loading 阶段)静态块就运行。其实不然:类加载(Loading)只是把字节码读入方法区;真正触发 static 块的是后续的「初始化(Initialization)阶段」,且仅当类被首次主动使用时才进入该阶段。
- 主动使用包括:new 实例、调用静态方法、访问非 final 静态字段、Class.forName("X")(默认 initialize=true)、子类初始化时父类未初始化等
- 反例:访问 static final 编译期常量(如 public static final int PORT = 8080;)不会触发初始化,static 块也就不会执行
- Class.forName("X") 会触发;ClassLoader.loadClass("X") 不会——这是常见调试盲点
误区二:以为“每次 new 就重跑 static 块”
静态块属于类级别,不是对象级别。哪怕你 new 一百次,只要类已完成初始化,static 块绝不再执行。
- 真正每次执行的是实例代码块({ ... })和构造方法
- 如果发现日志里 static 块反复打印,大概率是多个 ClassLoader 加载了同一个类(如 Web 应用热部署、OSGi 环境),此时每个 ClassLoader 视为独立类,各自初始化一次
- static 块中抛出未捕获异常(如 NullPointerException),会导致类进入“初始化失败”状态,之后任何引用都直接抛 ExceptionInInitializerError,且无法重试
误区三:混淆 static 块与静态变量初始化顺序
static 块和静态变量声明赋值(static int x = getValue();)按源码自上而下混合执行,不是“先全赋值再全执行块”。
- 若 getValue() 依赖后面才声明的静态字段,该字段值为默认值(0 / null),而非编译错误
- 父类 static 内容一定先于子类执行,且按文本顺序交错进行:父类静态变量 → 父类 static 块 → 子类静态变量 → 子类 static 块
- 不能在 static 块中用 this 或 new 当前类实例(编译报错:non-static context)
误区四:误把 static 块当“程序启动入口”
main 方法所在类的 static 块,并不会在 JVM 启动瞬间执行,而是在 main 方法被调用前、类初始化阶段才运行。
- 如果 main 方法没被调用(比如只反射加载类但不执行),static 块也不会触发
- 依赖外部资源(如读配置文件、连数据库)的 static 块,必须做容错处理,否则类可能永久不可用
- static 块适合做轻量、确定、无副作用的初始化(如预设常量、注册驱动),不适合做需重试或依赖运行时环境的操作
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











