java类初始化顺序核心是“静态优先、父类先行、源码顺序决定执行次序”:类加载时先执行父类静态变量与静态块(按源码顺序),再子类静态部分;对象创建时先父类实例变量与构造块,再父类构造方法,然后子类实例变量与构造块,最后子类构造方法。

Java类初始化顺序的核心是“静态优先、父类先行、源码顺序决定执行次序”。静态代码块不是可选的装饰,而是JVM在类初始化阶段(Initialization)自动触发的关键执行单元,它定义了类可用前的最小运行环境。
静态块执行时机与触发条件
静态块只在类首次被“主动使用”时执行一次,且线程安全。所谓主动使用包括:访问非编译期常量的静态字段、调用静态方法、new 实例、反射 Class.forName()、子类初始化时引用父类非final静态成员等。注意:ClassLoader.loadClass() 不触发初始化,static final 基本类型常量访问也不触发——这两类操作会跳过静态块。
- 执行发生在“准备阶段”之后:此时所有静态变量已分配内存并设默认值(0、null、false),静态块正是用来赋予真实值和执行副作用操作
- 若静态块抛出未捕获异常(如 NullPointerException),整个类进入“初始化失败状态”,后续任何访问都直接抛 NoClassDefFoundError
- 多个静态块按源码从上到下严格顺序执行,不存在“覆盖”或“延迟生效”概念
静态块与静态变量的协作关系
静态变量赋值语句(static int x = init();)和静态块(static { ... })在字节码中统一编译为
- 谁写在前面,谁先运行:若 static String a = getValue(); 出现在 static { b = 1; } 上方,则 getValue() 必然先调用
- 禁止前向引用:静态块内不能读取其后才声明的静态变量(编译报错),但可读取已声明变量的默认值(如 int 类型为 0)
- final static 基本类型常量(如 public static final int PORT = 8080)是编译期常量,值被内联,不参与类初始化流程
继承场景下的分层初始化保障
父子类静态部分强制分层执行,这是双亲委派机制的底层保证,无法绕过或颠倒。
- 顺序固定为:父类静态变量 → 父类静态块 → 子类静态变量 → 子类静态块
- 子类静态块可安全使用父类 public static 字段;反之,父类静态块绝不可依赖子类静态状态
- 该机制确保基础环境(如父类配置、驱动注册)就绪后,子类才能开展定制化初始化
适合静态块的典型环境准备任务
静态块适合做一次性、全局性、无实例依赖的启动准备,但必须规避异常、强耦合与耗时操作。
- 配置加载:从 classpath 读取 /config.yaml,解析后存入 static final Map 或自定义 ConfigHolder
- 驱动注册:显式 Class.forName("com.mysql.cj.jdbc.Driver")(JDBC 4.0+ SPI 兜底场景)
- 缓存预热:初始化 static final ConcurrentHashMap 并填充基础数据
- 禁止行为:调用 this、new 当前类实例、访问非静态成员、执行网络/IO 等阻塞操作
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











