<init>是实例构造方法,每次创建对象时执行,负责实例变量赋值、非静态代码块和构造器逻辑;<clinit>是类构造方法,类首次主动使用时执行且仅一次,负责静态变量赋值和静态代码块,按源码顺序合并,JVM保证线程安全。

init 和 clinit 是 Java 编译器自动生成的两个特殊字节码方法,名字不合法、不能在源码中直接调用,但它们分别承担对象级和类级初始化的核心职责,执行时机、内容来源、触发条件和执行次数都完全不同。
init 是对象构造器,每次 new 都执行一次
它对应源码中的每个构造方法(包括编译器补全的默认构造器),负责把实例变量赋值、非静态代码块、构造器逻辑“收敛”进一个方法里执行:
- 执行顺序固定:先调父类 init,再执行本类的实例变量赋值 → 实例代码块 → 构造器语句
- 触发场景包括:
new MyClass()、Constructor.newInstance()、clone()、反序列化 - 实例变量在类加载「准备阶段」已被设为默认值(如
int=0、Object=null),init 才真正运行你写的初始化逻辑 - 一个类可有多个 init(对应不同构造签名),每次创建新对象都会选一个执行
clinit 是类构造器,整个 JVM 生命周期只执行一次
它由编译器自动合成,仅包含静态变量显式赋值和 static{} 块,按源码书写顺序合并成单一方法:
- 执行时机是类加载五步中的「初始化阶段」,即第一次主动使用该类时(如访问静态字段、调用静态方法、
Class.forName()) - JVM 保证线程安全:多线程同时触发,只有一个线程执行 clinit,其余阻塞等待
- 父类 clinit 先于子类执行;若类没有静态变量或静态块,编译器根本不会生成 clinit
-
static final基本类型或字符串字面量会在「链接阶段」直接内联赋值,不进入 clinit;但new String("x")这类运行时创建的值仍需 clinit 执行
二者不在同一生命周期环节,不能互相替代
clinit 属于类加载过程,影响的是整个类的静态上下文;init 属于对象创建过程,每次 new 都新建一份独立状态:
- 执行顺序上,clinit 一定早于 init:new 一个对象前,必须确保其所属类已完成初始化(可能触发 clinit)
- 静态字段在「准备阶段」分配内存并设默认值,到 clinit 才被赋予你写的初值(如
static int x = 5;中的=5) - 查看字节码可用
javap -v,会明确看到<init>和<clinit>两个方法签名,前者带参数(如(I)V),后者无参且返回 void
常见误判点:哪些情况不会生成 clinit
不是所有含 static 的类都有 clinit:
- 只有静态变量有显式赋值语句(
static int a = 1;)或存在static{}块,才会生成 -
static final int a = 1;或static final String s = "hello";属于编译期常量,在链接阶段完成赋值,不进 clinit - 纯声明(
static int a;)或仅含注释的static{},编译器通常省略 clinit

















