java中类初始化与对象实例化严格分离:静态初始化()仅在类首次主动使用时执行一次,包括静态变量赋值和static代码块;实例初始化()每次new对象时都执行,负责实例变量、实例代码块和构造器逻辑,且遵循“父类先于子类、静态先于实例、声明顺序优先”的固定顺序。

Class 构造函数(即实例构造器,对应 JVM 中的 <init></init>)和类静态初始化(对应 JVM 中的 <clinit></clinit>)是两个完全独立、分属不同阶段的机制,执行时机有本质区别。
静态初始化只在类首次主动使用时触发一次
静态初始化包括:静态变量显式赋值 + static 代码块,它们被编译器合并进 <clinit></clinit> 方法。这个方法不是你写的任何方法,而是 JVM 自动生成的,且仅在类**首次被主动使用**时执行,且仅一次。
- 主动使用的典型场景包括:调用该类的静态方法、访问非编译期常量的静态字段、new 该类实例、通过反射获取 Class 对象、初始化子类(且父类尚未初始化)
- 注意:访问编译期常量(如 public static final int X = 123;)不会触发
<clinit></clinit>,因为其值已在编译时内联到调用处 - 多个 static 块和静态变量按源码顺序合并执行,JVM 保证线程安全(由类加载器加锁)
构造函数每次 new 实例时都执行
构造函数(<init></init>)属于实例初始化阶段,与静态初始化无关。每次执行 new 指令创建对象时,JVM 都会调用对应的 <init></init> 方法。
- 它不处理 static 内容,只负责:实例变量显式赋值、实例代码块({})、以及构造器体内的逻辑
- 执行顺序上,它总在父类构造器之后、子类实例代码块之后(若存在)——准确说是:父类实例变量/块 → 父类构造器 → 子类实例变量/块 → 子类构造器
- 即使类已加载并完成静态初始化,只要 new 新对象,就一定走一遍
<init></init>
二者不重叠,也不嵌套,但有明确先后关系
当第一次 new 某个类的实例时,会**先触发静态初始化(若尚未发生)**,再进入实例初始化流程。后续再 new 同一类对象,跳过静态部分,直接执行 <init></init>。
- 例如:第一次 new Child() 会依次执行:父类静态 → 子类静态 → 父类实例块 → 父类构造器 → 子类实例块 → 子类构造器
- 第二次 new Child() 则跳过前两步(静态部分),直接从父类实例块开始
- 若此前已通过 Child.someStaticMethod() 触发过静态初始化,则第一次 new 也不会再跑
<clinit></clinit>
常见误区提醒
容易混淆的点往往来自对“类加载”和“对象创建”的界限不清:
- 类加载(Loading)≠ 类初始化(Initialization):加载只是把字节码读入内存;初始化才是执行
<clinit></clinit>的阶段 -
<clinit></clinit>不是构造器,也不参与对象创建;<init></init>不操作静态成员,也不影响其他实例 - 子类初始化不一定导致父类
<clinit></clinit>再执行——父类若早已初始化完毕,就不再重复











