02_对象实例化流程(内存分配 初始化)

一、对象实例化概述

对象实例化是 Java 中创建对象的过程,通过new关键字触发,核心目标是在内存中为对象分配空间并完成初始化,最终得到一个可用的对象。整个流程可概括为:类加载检查→内存分配→初始化→返回对象引用,每个步骤都由 JVM 自动完成,确保对象的完整性和可用性。

在这里插入图片描述

二、对象实例化的核心流程

2.1 步骤 1:类加载检查

在执行new 类名(...)时,JVM 首先会检查该类是否已被加载到方法区:

  • 若类未加载:JVM 会先执行类加载流程(加载、验证、准备、解析、初始化),将类的信息(如属性、方法、构造器定义)加载到方法区。
  • 若类已加载:直接进入下一步。

作用:确保对象对应的类信息已存在,为后续内存分配和初始化提供依据。

示例

// 执行new Person()时,JVM先检查Person类是否已加载
Person p = new Person(); 

2.2 步骤 2:内存分配

类加载检查通过后,JVM 会在堆内存中为新对象分配空间,空间大小由类的属性(成员变量)数量和类型决定(如int占 4 字节,Object引用占 8 字节等)。

内存分配的核心操作

  1. 确定内存大小:根据类加载后确定的属性信息,计算出单个对象所需的总内存空间。
  2. 划分内存区域:从堆中划分出对应大小的连续空间给新对象,常用两种方式:
    • 指针碰撞:若堆内存是连续的(无碎片),通过移动指针直接划分空间(如 Serial、ParNew 等收集器)。
    • 空闲列表:若堆内存有碎片,JVM 通过维护 "空闲内存块列表",从中选择合适的块分配(如 CMS 收集器)。

2.3 步骤 3:初始化(核心阶段)

内存分配完成后,JVM 会对对象进行多阶段初始化,确保对象的属性有合理的初始值。初始化按以下顺序执行:

2.3.1 阶段 1:默认初始化(零值初始化)

JVM 自动为对象的所有属性赋默认值(与属性类型相关),确保对象在显式初始化前不会有垃圾值。
常见类型的默认值:

  • 基本类型:int→0、boolean→false、double→0.0等。
  • 引用类型:null(如String→null、Object→null)。

示例

public class Person {
    private int age; // 默认初始化:age=0
    private String name; // 默认初始化:name=null
}

// 执行new Person()时,JVM先为age赋0,name赋null

2.3.2 阶段 2:显式初始化(代码定义时的初始化)

执行类中属性定义时显式指定的初始值(若有),覆盖默认初始化的值。

示例

public class Person {
    private int age = 18; // 显式初始化:覆盖默认值0→18
    private String name = "未知"; // 显式初始化:覆盖默认值null→"未知"
}

// 显式初始化在默认初始化之后执行,因此age最终为18,name为"未知"

2.3.3 阶段 3:构造器初始化(执行构造器代码)

调用类的构造器,执行构造器中的代码,进一步初始化对象(如根据参数修改属性值),这是对象初始化的最后一步。

示例

public class Person {
    private int age = 18; // 显式初始化
    private String name;
    
    // 构造器
    public Person(String name, int age) {
        this.name = name; // 构造器初始化:为name赋值
        this.age = age; // 构造器初始化:覆盖显式初始化的18
    }
}

// 执行new Person("张三", 20)时:
// 1. 默认初始化:age=0,name=null
// 2. 显式初始化:age=18(覆盖默认值)
// 3. 构造器初始化:name="张三",age=20(覆盖显式初始化的值)

初始化顺序总结:默认初始化 → 显式初始化 → 构造器初始化(后续步骤的初始化会覆盖前序结果)。

2.4 步骤 4:返回对象引用

初始化完成后,JVM 会将堆中对象的内存地址(引用)返回给变量,此时变量指向堆中的对象,对象实例化完成,可通过变量操作对象。

示例

// p接收对象的引用(堆中地址),p指向新创建的Person对象
Person p = new Person("张三", 20); 

三、完整示例与流程解析

以Student类为例,完整展示对象实例化流程:

public class Student {
    // 属性定义
    private String school; // 默认初始化:null
    private int grade = 1; // 显式初始化:1(覆盖默认值0)
    private String name; // 默认初始化:null
    
    // 构造器
    public Student(String name, int grade) {
        this.name = name; // 构造器初始化:name赋值
        this.grade = grade; // 构造器初始化:覆盖显式初始化的1
    }

    public static void main(String[] args) {
        // 实例化对象
        Student s = new Student("李四", 3);
    }
}

流程解析

  1. 类加载检查:JVM 检查Student类是否加载,未加载则先加载。
  2. 内存分配:在堆中为Student对象分配空间(存储school、grade、name三个属性)。
  3. 初始化
    • 默认初始化:school=null,grade=0,name=null。
    • 显式初始化:grade=1(覆盖默认值 0)。
    • 构造器初始化:name="李四",grade=3(覆盖显式初始化的 1)。
  4. 返回引用:堆中对象的地址赋值给s,s指向该对象,实例化完成。

四、注意事项

  1. 初始化的不可跳过性:无论是否显式定义构造器,JVM 都会执行默认初始化和显式初始化,确保对象属性有合理初始值。
  2. 构造器的作用:构造器不负责分配内存(由 JVM 完成),仅用于最终的初始化(如根据参数定制对象)。
  3. 引用与对象的分离:变量存储的是对象的引用(地址),而非对象本身,对象始终存储在堆中。
  4. 实例化失败的情况:若类未找到(类加载失败)、内存不足(堆空间耗尽)或构造器执行异常,会导致实例化失败,抛出对应异常(如ClassNotFoundException、OutOfMemoryError)。

五、总结

Java 对象实例化是一个由 JVM 主导的有序过程,核心流程可归纳为:

  1. 类加载检查:确保类信息可用;
  2. 内存分配:在堆中为对象划分空间;
  3. 多阶段初始化:按 "默认初始化→显式初始化→构造器初始化" 的顺序完成属性赋值;
  4. 返回引用:将对象地址返回给变量,对象可用。

理解实例化流程有助于开发者清晰掌握对象的创建机制,避免因初始化顺序错误导致的逻辑问题(如在构造器中使用未初始化的属性)。

posted @ 2025-07-07 08:20  HuCiZhi  阅读(95)  评论(0)    收藏  举报