new 创建对象时先加载类,再在堆中分配内存并初始化为零值,设置对象头,调用构造方法,最后将引用赋给栈中变量;堆存对象实体,栈存引用。

在 Java 中,new 关键字是创建对象最常用的方式,它会触发对象在堆内存中分配空间、调用构造方法完成初始化,并返回该对象的引用。
new 的执行过程
当执行类似 Person p = new Person("Alice"); 这样的语句时,JVM 会按以下步骤操作:
- 检查
Person类是否已被加载;若未加载,先触发类加载(加载、链接、初始化) - 在堆内存中为新对象分配足够大小的连续内存空间(具体大小由类的字段决定)
- 将分配的内存空间初始化为零值(如 int → 0,Object 引用 → null),保证字段有默认值
- 设置对象头信息(包括哈希码、GC 分代年龄、锁状态、类型指针等)
- 调用指定的构造方法(如
Person(String name)),对字段进行显式赋值和逻辑初始化 - 将新对象的引用(即内存地址)赋给栈中的变量
p
堆内存与栈内存的角色分工
堆(Heap) 是所有线程共享的运行时内存区域,用于存放对象实例和数组。new 创建的对象实体一定在堆中。
栈(Stack) 是线程私有的,每个方法调用对应一个栈帧,其中存储局部变量(包括对象引用)和方法参数。变量 p 本身存于栈中,它保存的是堆中那个 Person 对象的地址。
立即学习“Java免费学习笔记(深入)”;
例如:String s = new String("hello"); —— 字符串对象在堆中,而 s 这个引用在当前方法的栈帧里。
注意:new 不等于“一定新建”
某些情况下,new 看似创建了对象,但实际可能被 JVM 优化或与常量池协同工作:
- 对于字符串字面量(如
new String("abc")),JVM 仍会先检查字符串常量池是否有 "abc";若已有,则堆中仍会新建一个对象(内容相同但地址不同),这是两个独立对象 - 逃逸分析开启时,JVM 可能将未逃逸的对象分配在栈上(标量替换),但这属于 JIT 优化,对开发者透明,代码层面仍视为“堆中创建”
- 使用
new总是会调用构造方法,哪怕该构造方法为空;这与反序列化、反射allocateInstance或克隆等不走构造器的方式有本质区别
常见误区提醒
不要混淆“引用变量声明”和“对象创建”:
-
Person p;—— 只在栈中声明引用,未指向任何堆对象(值为null) -
Person p = new Person();—— 才真正触发堆内存分配和对象初始化 -
Person p = null;—— 明确赋空引用,依然没创建对象
另外,new 创建的对象生命周期由垃圾回收器管理,当没有强引用可达时,它才可能被回收。


















