java数组是堆中连续存储的对象,栈中仅存指向它的地址;基本类型数组存值,引用类型数组存地址;传参和赋值均为地址拷贝,共享同一堆内存。

Java数组不是简单的一组值,而是一个完整的对象,它的存储结构有明确的层次和位置关系。理解它,关键在于分清“谁在栈里、谁在堆里、存的是值还是地址”。
数组变量在栈中,只存一个地址
写 int[] arr; 时,JVM只在当前方法的栈帧里分配一个小空间(4或8字节),里面什么都没有,值是 null。这时候数组还没诞生,不能访问任何元素。只有执行 arr = new int[5]; 后,这个栈槽才被填入一个十六进制地址(比如 0x1234abcd),它指向堆里那块真正存放数据的地方。
数组本体在堆中,结构固定且连续
new 出来的数组对象完整存在于堆内存,布局严格分为三段:
- 对象头(约12–16字节):含GC标记、锁信息、类型元数据
- length字段(4字节):记录数组长度,不可修改
- 数据区:紧接着 length,连续存放所有元素
例如 int[3] 实际占用约 28 字节(12+4+3×4),且这 28 字节物理地址是连在一起的——这是数组支持高效随机访问的根本原因。
基本类型数组 vs 引用类型数组:存的内容不同
两者都在堆上,但数据区内容本质不同:
- int[]、double[] 等:数据区直接存数值(如 1、2、3)
- String[]、User[] 等:数据区存的是引用地址(每个元素可能是 null,也可能指向堆中另一个对象)
比如 String[] s = new String[2]; s[0] = "hello";,堆里实际存在两个独立对象:一个是长度为2的数组对象,另一个是字符串常量池或堆中的 "hello" 实例,s[0] 存的只是后者的地址。
传参和赋值的本质:地址拷贝 ≠ 对象复制
向方法传数组,传递的是栈中那个地址的副本:
- arr[i] = x → 修改堆中同一块内存,原数组立刻可见变化
- arr = new int[10] → 只让形参指向新地址,原变量仍指向旧数组
- int[] a = b; → a 和 b 共享同一块堆内存,任一方改元素,对方同步感知
这种行为不是“数组特殊”,而是 Java 所有引用类型共有的语义:操作的是地址,不是数据本身。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











