Java类与对象初始化严格分阶段:类加载(加载→验证→准备→解析→初始化)后,对象创建执行分配内存、零值初始化、设置对象头、调用构造器;字段初始化顺序为静态→实例→构造体。

Java 中类和对象的初始化与内存分配不是一步到位的操作,而是一套有严格顺序、分阶段执行的机制。理解它,能避开 NullPointerException、静态块不执行、子类访问父类未初始化字段等典型问题。
类加载是前提:先有类,才能造对象
类必须在首次使用时完成加载与初始化,否则 new 操作无法进行。触发时机包括:
- 第一次用 new 创建该类实例
- 第一次访问该类的静态字段(非 final 常量)或调用静态方法
- 反射调用(如 Class.forName())
- 初始化子类时,若父类尚未初始化,会先触发父类初始化
类加载过程包含五个阶段:加载 → 验证 → 准备 → 解析 → 初始化。
其中「准备」阶段为 static 字段分配内存并设默认值(如 int a 设为 0,String s 设为 null);
「初始化」阶段才真正执行 static 块和静态变量赋值语句(按代码顺序从上到下)。
对象创建四步走:分配 → 零值 → 设置头 → 调构造
当类已加载,执行 A a = new A(); 时,JVM 实际完成以下四步:
- 堆内存分配:根据类结构计算大小(对象头 + 实例字段 + 对齐填充),在堆中划出空间。方式取决于 GC 策略——规整堆用「指针碰撞」,碎片化堆用「空闲列表」;多线程下通过 TLAB(线程本地缓冲)或 CAS 保证安全
-
零值初始化:将分配的内存全部清零(不执行任何 Java 赋值语句)。此时所有字段已有确定初始值:基本类型为 0/false,引用类型为
null -
设置对象头:写入 Mark Word(含哈希、锁状态、GC年龄等)和类型指针(指向方法区中的
A.class元数据) -
执行
<init>方法:即你写的构造方法。执行顺序固定:
→ 隐式/显式调用父类构造器(super())
→ 执行父类实例变量赋值和实例初始化块
→ 执行父类构造方法体
→ 执行子类实例变量赋值和实例初始化块
→ 执行子类构造方法体
字段初始化顺序不能错:静态 → 实例 → 构造
整个初始化流程中,各类成员的执行次序严格固定,直接影响程序行为:
- 父类静态变量和静态块(按代码顺序)
- 子类静态变量和静态块(按代码顺序)
- 父类实例变量和实例块(按代码顺序)
- 父类构造方法
- 子类实例变量和实例块(按代码顺序)
- 子类构造方法
注意:实例变量的「声明时赋值」(如 int x = 5;)和「实例初始化块」都属于第3/5步,在构造方法体之前执行;而构造方法内的赋值(如 this.x = 10;)发生在最后。若在父类构造器中提前访问子类字段,拿到的是零值(如 0 或 null),因为子类实例部分尚未初始化。
立即学习“Java免费学习笔记(深入)”;
常见陷阱与实践建议
这些细节看似底层,但直接决定代码是否健壮:
- 写了带参构造后,无参构造不会自动生成——需手动补全,否则
new A()编译失败 - 别在构造方法里把
this传出去(如注册监听、启动线程),此时对象可能处于半初始化状态 - 静态资源(如文件句柄、数据库连接)不随对象销毁自动释放,必须显式关闭或用 try-with-resources
- 大对象(如超大数组)在 G1 GC 下可能直接进入 Humongous Region,影响分配效率,要注意监控
- 栈中只存引用(如
A a),对象实体永远在堆中;a是地址,不是对象本身


















