java中类加载与对象初始化分属不同阶段:类需先完成加载和静态初始化,才能创建实例;每次new都会执行实例初始化。静态部分按父类到子类、文本顺序执行;实例部分每次new都按父类实例块→父类构造器→子类实例块→子类构造器顺序执行。

Java 中引用类型的类加载顺序与对象初始化,本质上是两个紧密关联但阶段分明的过程:类必须先完成加载和初始化,才能创建其实例;而每次创建对象,又会触发实例层面的初始化流程。关键在于分清「类级别」和「实例级别」的执行时机与依赖关系。
类加载与初始化的触发时机
类不会在代码编译时或声明引用时就加载,而是在首次主动使用时才触发完整加载(加载→验证→准备→解析→初始化)。以下操作属于主动使用,会强制初始化类:
- 用
new创建该类或其子类的实例 - 访问该类的静态字段(非编译期常量,即非
static final基本类型/字符串字面量) - 调用该类的静态方法
- 通过反射(如
Class.forName("X"))加载类 - 初始化某个子类时,若其父类尚未初始化,则先初始化父类
- JVM 启动时指定的主类(含
main方法)
注意:Parent p; 这样的声明、或 Parent[] arr = new Parent[5]; 这类数组创建,都属于被动引用,仅触发类加载(到准备阶段),不执行静态初始化块或静态变量显式赋值。
静态成员的初始化顺序(类级别)
当类被初始化时,JVM 按照源码中出现的文本顺序执行静态变量赋值和静态代码块,且遵循「父类优先」原则:
- 先递归初始化父类的静态部分(静态变量默认值在「准备」阶段已设为 0/null;显式赋值和
static{}在「初始化」阶段按序执行) - 再执行子类自身的静态变量初始化和静态代码块
例如:new Child() 会依次输出:
Grandparent静态块 → Parent静态块 → Child静态块
对象创建时的实例初始化顺序
每次执行 new X(),都会在堆中分配内存(所有字段初始化为默认值:0、false、null),然后按以下固定顺序完成实例初始化:
- 执行父类的实例变量赋值和实例代码块(
{...}) - 执行父类构造器(隐式或显式调用
super()) - 执行子类的实例变量赋值和实例代码块
- 执行子类构造器
这个过程与静态初始化完全分离——静态部分只走一次,实例部分每次 new 都走一遍。构造器内部实际隐含了对父类构造器和当前类实例块的调用链。
引用类型与类加载的实际影响
所谓“引用类型”的写法(如 List<string> list = new ArrayList();</string>)本身不直接决定加载顺序,但它可能间接触发多个类的初始化:
-
ArrayList类被new触发初始化(包括其父类AbstractList等) - 泛型擦除后不产生新类,但若代码中显式引用了
String的静态成员(如String.CASE_INSENSITIVE_ORDER),则也会触发String初始化(不过String通常早已由 Bootstrap ClassLoader 预加载) - 接口的静态字段(如
Collection.EMPTY_LIST)访问,同样会触发对应接口的初始化
因此,真正影响顺序的是「哪个类的哪个静态成员被首次主动访问」,而不是引用声明的形式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











