用 “建房子” 讲懂 Android 中 new 对象的全过程:从代码到 ART 的魔法

咱们先编个小故事:你是 Android 小白 "小安",想做个「用户信息 App」------ 需要一个能存 "用户名" 和 "年龄" 的 "专属小屋"(对应代码里的User对象)。你写了句User user = new User("小安", 25);,按下运行键后,手机里的 "大管家"(ART 虚拟机,Android 现在不用 JVM 了,但核心逻辑相通)就开始帮你 "盖房子"。

下面咱们跟着这个 "盖房子" 的故事,一步步拆透 new 对象的原理,最后再用代码和时序图固化认知。

一、先准备 "盖房子的图纸":User 类是什么?

在 new 对象前,你得先告诉 "大管家":"我要的房子长这样!"------ 这就是User类的作用,它是对象的 "设计图纸"

先看你写的 "图纸代码":

java 复制代码
// 这是"用户小屋"的设计图纸:User类
class User {
    // 图纸里规定的"房间":用户名、年龄(成员变量)
    String name;
    int age;

    // 图纸里规定的"装修方案":构造方法(必须和类名一样,没有返回值)
    public User(String name, int age) {
        this.name = name; // 给"用户名房间"贴标签
        this.age = age;   // 给"年龄房间"填数字
    }
}

// 你(调用者)发起"盖房子"请求
public class Main {
    public static void main(String[] args) {
        // 关键一步:喊"大管家"按图纸盖房子!
        User user = new User("小安", 25); 
    }
}

你以为new User(...)是直接 "动工盖房"?错!"大管家" ART 很严谨,动工前要做 5 件事,咱们继续看故事。

二、"盖房子" 的 5 步魔法:new 对象的完整流程

咱们把 ART 大管家的工作拆解成 5 个步骤,每个步骤都对应 "盖房子" 的真实场景,再贴代码解释:

步骤 1:查图纸 ------ART 检查 "User 类是否已加载"

你喊 "盖 User 小屋",大管家第一反应是:"我手里有 User 的图纸吗?"------ 这对应类加载检查 :ART 会先去 "类缓存区" 查User.class是否已被加载成Class对象(图纸备案)。

  • 如果没备案(类未加载):大管家会立刻执行「类加载流程」(3 小步),相当于 "找图纸→审图纸→备案图纸":

    1. 加载 :去 APK 的 dex 文件里找User.class(找图纸原件),把二进制数据读进内存,生成一个Class对象(图纸复印件备案)。

    2. 链接:审图纸 + 做准备:

      • 验证:检查图纸有没有错(比如语法错误、是否符合 Android 规范),防止 "豆腐渣工程"。
      • 准备:给静态变量分配内存并设默认值(比如static int count = 10会先设为 0,等初始化时再改成 10)。
      • 解析:把图纸里的 "符号引用"(比如String)换成内存里的 "直接引用"(找到String类的实际地址)。
    3. 初始化 :执行静态代码块(比如static { count = 10; }),给静态变量赋真实值 ------ 这步完成,图纸才算正式备案。

  • 如果已备案(类已加载):直接跳过类加载,进入下一步。

步骤 2:找空地 ------ART 在堆内存分配空间

图纸备案好了,大管家要带你来 "土地储备库"------堆内存(Android 里所有对象都住在堆里),给你划一块地盖房子。

怎么划地?分两种情况,像极了现实中找地:

分配方式 场景比喻 原理说明
指针碰撞 小区里的地是 "整齐排空" 的 堆内存里的空闲空间是连续的,大管家把 "空闲指针" 往前挪一块(挪的大小 = User 对象需要的内存),挪出的空间就是你的地。
空闲列表 小区里的地是 "东一块西一块" 堆内存碎片化,大管家查 "空闲土地记录表",找一块大小刚好的地划给你。

问题来了:如果有 10 个线程同时喊 "盖房子",抢同一块地怎么办?(并发安全问题)大管家有两个解决办法:

  1. CAS + 失败重试:大管家盯着抢地过程,谁先抢到算谁的,没抢到的重试(CAS 是 "Compare And Swap",比较并交换,保证原子性)。
  2. TLAB(本地线程分配缓冲) :大管家提前给每个线程分一块 "专属小空地",线程盖房子先在自己的小空地里划,用完了再向大管家要 ------ 避免抢地,效率更高(Android 默认用这个)。

步骤 3:毛坯房 ------ART 执行 "零值初始化"

地划好了,大管家不会让你直接装修,而是先把地弄成 "毛坯房":给所有成员变量赋默认零值

对应代码里的User类:

  • name(String 类型)默认设为null(空房间);
  • age(int 类型)默认设为0(空数字)。

为什么要做这步?防止 "房间里有垃圾"!比如如果不设零值,age可能是内存里残留的随机数(比如 12345),导致 App 显示错误。

步骤 4:挂门牌 ------ART 设置 "对象头"

毛坯房弄好,大管家要在门口挂个 "门牌"------对象头(Object Header) ,这是每个对象的 "身份证",里面存 3 类关键信息:

信息类别 门牌内容举例 作用
类元信息 "户型:User" 告诉 ART:这个对象是哪个类的实例(能找到 User 类的Class对象)。
对象标识信息 "门牌号:0x123456"(哈希码) 唯一标识这个对象,GC(垃圾回收)时用来认对象。
锁状态信息 "未上锁" 处理多线程同步(比如synchronized锁就靠这个)。

比如你的 User 对象,门牌上会写:"户型 User,门牌号 0x123456,未上锁"------ART 一看门牌就知道这房子的所有基础信息。

步骤 5:精装修 ------ART 调用构造方法()

最后一步!大管家按照你写的 "装修方案"------构造方法(User 的构造函数) ,给毛坯房做 "精装修"。

对应代码:

java 复制代码
// 构造方法执行:给成员变量赋真实值(装修)
public User(String name, int age) {
    this.name = name; // 把"小安"贴到name房间(从null→"小安")
    this.age = age;   // 把25填到age房间(从0→25)
}

这里要注意:构造方法不是 "创建对象",而是 "初始化对象" !对象在步骤 2 分配内存时就已经 "诞生" 了,构造方法只是给它填内容。

等构造方法执行完,"用户信息小屋" 就彻底建好啦!大管家把 "房子的地址"(对象引用,比如0x123456)交给你(赋值给user变量),以后你通过user.nameuser.age,就能根据地址找到房子里的内容。

三、时序图:一眼看清整个调用过程

为了更直观,咱们用时序图把 "调用者→ART→堆内存" 的交互过程画出来(用 Mermaid 语法,你可以复制到支持 Mermaid 的工具里查看):

堆内存User.class堆内存(土地库)User.class(图纸)ARTART(大管家)调用者(Main线程)堆内存User.class堆内存(土地库)User.class(图纸)ARTART(大管家)调用者(Main线程)alt[未加载]类加载子系统内存分配模块对象初始化模块发起new User("小安",25)请求检查User类是否已加载?加载User.class(读图纸)链接(验证→准备→解析)初始化(执行静态代码)User类加载完成(备案图纸)内存分配模块:用TLAB/指针碰撞分配空间在堆中划分User对象所需空间内存分配模块:给成员变量设零值(name=null, age=0)对象初始化模块:设置对象头(类元/哈希码/锁状态)调用User构造方法()执行构造方法:name="小安", age=25返回对象引用(如0x123456)给user变量

四、必记的 3 个关键结论

  1. new 对象不是一步到位:是 "查图纸→找地→毛坯→挂门牌→装修"5 步,构造方法是最后一步。
  2. 对象住堆,引用住栈 :"房子"(对象)在堆内存,"房子地址"(user引用)在栈内存(调用者线程的栈)。
  3. 类只加载一次 :同一个类(比如 User)在 App 运行期间,ART 只会加载一次,后续 new 对象直接复用已加载的Class对象。

通过这个 "盖房子" 的故事,你应该能看懂 new 对象的底层逻辑了吧?下次再写new的时候,就想想背后有个 ART 大管家在帮你忙前忙后~

相关推荐
手机不死我是天子8 小时前
《Android 核心组件深度系列 · 第 4 篇 ContentProvider》
android·架构
鹏多多8 小时前
flutter-切换状态显示不同组件10种实现方案全解析
android·前端·ios
FengyunSky9 小时前
高通Camx内存问题排查
android·linux·后端
00后程序员张10 小时前
苹果软件混淆的工程逻辑,从符号空间到资源扰动的体系化实现
android·ios·小程序·https·uni-app·iphone·webview
alexhilton18 小时前
突破速度障碍:非阻塞启动画面如何将Android 应用启动时间缩短90%
android·kotlin·android jetpack
kobe_OKOK_18 小时前
Django `models.Field` 所有常见配置参数的完整清单与说明表
android
前行的小黑炭20 小时前
Android Compose :初步了解一下生命周期,对比原生android
android·kotlin·app
湖南人爱科技有限公司21 小时前
RaPhp和Python某音最新bd-ticket-guard-client-data加密算法解析(视频评论)
android·python·php·音视频·爬山算法·raphp
守城小轩1 天前
Chromium 138 编译指南 - Android 篇:从Linux版切换到Android版(六)
android·chrome·指纹浏览器·浏览器开发·超级浏览器