在 Java 中,普通对象(非数组、非基本类型)的实例化过程通过 new 关键字触发,主要分为以下步骤:
类加载检查
- JVM 检查类是否已被加载(ClassLoader 加载过程)。
- 若未加载,则先执行类加载(加载 → 验证 → 准备 → 解析 → 初始化)。
内存分配
- JVM 在 堆内存 中为对象分配空间(对象大小在类加载时已确定)。
- 分配方式:
- 指针碰撞(堆内存规整时,移动指针划分空间)。
- 空闲列表(堆内存不规整时,从空闲内存块列表分配)。
初始化默认值
- 为对象的所有成员变量赋予类型默认值:
- int/long/short/byte → 0
- float/double → 0.0
- boolean → false
- 引用类型 → null
设置对象头(Object Header)
- 在对象内存布局中写入元数据:
- Mark Word:哈希码、GC 分代年龄、锁状态等。
- 类型指针:指向方法区中的类元数据(Class Metadata)。
- 若启用压缩指针(-XX:+UseCompressedOops),类型指针占 4 字节,否则占 8 字节。
执行初始化代码
- 按顺序执行以下初始化操作:
- 实例变量显式赋值(如 private String name = "Tom";)。
- 实例初始化块(Instance Initializer Block):
{
System.out.println("执行实例初始化块");
}
- 构造函数(Constructor):
- 先隐式调用父类构造函数(super()),递归至 Object 类。
- 再执行子类构造函数的剩余代码。
关键流程图解
new 关键字触发
↓
类加载检查(未加载则加载类)
↓
在堆中分配内存
↓
初始化成员变量为默认值(0/null/false)
↓
设置对象头(Mark Word + 类型指针)
↓
执行初始化代码:
1. 显式赋值(name = "Tom")
2. 实例初始化块 {}
3. 构造函数(先 super() → 再子类构造代码)
示例代码分析
class Parent {
int a = 10; // 显式赋值
{ System.out.println("Parent 实例块"); }
Parent() { System.out.println("Parent 构造函数"); }
}
class Child extends Parent {
int b = 20; // 显式赋值
{ System.out.println("Child 实例块"); }
Child() {
super(); // 隐式调用父类构造函数
System.out.println("Child 构造函数");
}
}
// 创建对象
Child obj = new Child();
输出顺序:
Parent 实例块
Parent 构造函数
Child 实例块
Child 构造函数
内存初始化过程:
- 分配内存后,a=0, b=0(默认值)。
- 执行父类初始化:a=10 → 父类实例块 → 父类构造函数。
- 执行子类初始化:b=20 → 子类实例块 → 子类构造函数。
通过以上步骤,JVM 确保对象被正确初始化并处于有效状态,之后可通过引用操作对象。