java一维数组是“引用+连续内存块”组合:声明仅在栈创建null引用,初始化才在堆分配含对象头、length字段和数据体的连续空间,赋值传参均为地址拷贝,故修改共享数据但重赋值不改变原引用。

Java 一维数组不是“一块数据”,而是一个“引用+连续内存块”的组合:声明只在栈上留个地址变量,真正存数据的数组对象一定落在堆里,且长度固定、地址连续——这是它高效随机访问(O(1))的根源,也是它无法扩容、赋值即共享的根本原因。
声明只是占位,不分配实际空间
写 int[] arr; 时,JVM 只在当前方法的栈帧中创建一个名为 arr 的引用变量,初始值为 null。此时没有任何数组对象诞生,也没有堆内存被占用。这一步纯粹是语法约定,告诉编译器“将来会用一个 int 类型的一维数组”。常见错误如直接使用 arr[0] = 5; 就会触发 NullPointerException,因为栈上有名字,但堆上没东西可指。
初始化才触发堆内存分配
执行 arr = new int[5]; 或 int[] arr = {1,2,3}; 时,JVM 才真正在堆中申请连续空间。这块空间包含三部分:
- 对象头(Header):存储哈希码、GC分代年龄等元信息
- length 字段:4 字节整数,记录数组长度,可通过 arr.length 访问
- 数据体(Data):5 个连续的 int 值,每个占 4 字节,默认初始化为 0
分配完成后,堆中该数组的首地址(比如 0x7f8a...)被赋给栈中的 arr 变量。后续所有 arr[i] 访问,都是靠“首地址 + i × 4”直接算出物理位置,无需遍历或查表。
赋值与传参本质是地址拷贝
数组变量之间赋值(如 int[] b = arr;)或作为参数传入方法,传递的都是栈中那个地址值的副本,不是数据本身。这意味着:
- 修改 b[2] = 99; 会影响 arr[2],因为二者指向同一块堆内存
- 但在方法内写 arr = new int[10];,只会改变该方法栈帧里的局部变量指向,原调用方的 arr 仍指向旧数组
- 所谓“复制数组”,必须显式调用 Arrays.copyOf() 或手动循环赋值
默认值规则与类型差异要分清
未显式赋值的元素,按类型自动填充默认值:
- 基本类型:int/double/boolean 等填 0 / 0.0 / false
- 引用类型:String[]、Object[] 中每个槽位填 null,不代表有对象,只是空引用
特别注意:int[] 堆中存的是真正的数字;而 String[] 堆中存的是一串地址(每个指向一个 String 对象),这些 String 对象本身也在堆中,可能分散存放。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











