java一维数组内存布局为栈存引用、堆存数据:数组变量在栈中仅存堆地址,实际元素连续存储于堆;多变量指向同一堆地址时修改会联动;length是堆中对象的不可变元数据。

Java 一维数组的内存布局,核心在于栈存引用、堆存数据。数组变量本身(如 int[] arr)是局部变量,存于栈中;而它真正指向的连续内存块(含所有元素),一定在堆中分配。理解这点,就能看懂赋值、复制、修改为何有时“联动”、有时“独立”。
栈中只存一个地址,不是数据本身
当你写 int[] arr = new int[3];,JVM 做了两件事:
- 在堆中开辟一块连续空间(比如 12 字节,3 × 4 字节),每个位置初始化为 0;
- 在栈中创建变量
arr,里面存的不是数字,而是这块堆内存的起始地址(例如0x7a8b)。
所以打印 System.out.println(arr) 输出的是类似 [I@7a8b 的哈希码,本质就是这个地址的简写。你无法通过 arr 直接读取堆里内容——必须用 arr[0] 这样的索引,让 JVM 根据地址 + 偏移量去堆里查。
不同初始化方式,堆中行为一致
无论是动态还是静态初始化,最终都在堆中生成相同结构:
-
int[] a = new int[3];→ 堆分配 3 个int,全为 0; -
int[] b = {5, 6, 7};→ 堆分配 3 个int,分别存 5、6、7; -
String[] c = new String[]{"a", "b"};→ 堆分配 2 个引用位置(每个存 null 或字符串对象地址),字符串字面量本身在方法区常量池。
关键区别只在“谁来填值”:系统填默认值(动态),还是你直接填(静态)。但堆中那块连续内存的分配逻辑完全相同。
多个变量指向同一堆地址,修改会互相影响
这是最容易出错的地方。看这段代码:
-
int[] x = new int[]{1, 2, 3};→ 堆中新建数组,x 指向它; -
int[] y = x;→ 不新建数组!只是把 x 的地址(比如0x7a8b)复制给 y; -
y[1] = 99;→ JVM 根据 y 存的地址,去堆中0x7a8b + 4处写入 99; - 此时
x[1]也变成 99 —— 因为 x 和 y 指的是堆里同一块内存。
只有当重新赋值 y = new int[]{4, 5, 6};,y 才会指向新堆地址,与 x 彻底分离。
length 是数组对象自带的属性,存在堆里
数组是对象,arr.length 不是计算出来的,而是 JVM 在创建数组时就写死在堆中对象头之后的一个固定偏移位置。所以:
- 访问
arr.length极快,不涉及遍历或计算; - 它不可修改 —— 没有
setLength()方法,因为长度属于数组元数据,和元素一样固化在堆中; - 这也是为什么数组扩容必须新建数组:旧对象的 length 无法变更,只能换一个新对象。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











