数组参数传递的是引用副本,但基本类型元素操作本质是值传递:每次arr[i]读写都是对堆中固定位置的值拷贝,因位置共享故修改对外可见。

Java 中数组作为参数传递时,基本数据类型元素的“按值传递”本质,关键不在于数组本身怎么传,而在于数组里存的是什么、怎么被访问和修改。
数组变量传递的是引用副本,但元素操作仍是值传递
数组是对象,所以数组变量(如 int[] arr)作为参数传递时,传的是该数组对象在堆内存中的地址的副本。形参和实参指向同一个数组对象——这点没错。但注意:真正被读写的是数组里的每个元素,而这些元素如果是基本类型(比如 int),它们本身存储在堆中数组对象内部,每次读取或赋值,都是一次独立的值拷贝行为。
例如:
-
arr[0] = 100;这行代码不是“把引用传进去再改引用”,而是:通过引用定位到数组对象 → 找到下标为 0 的内存槽 → 把整数100的值复制进去; -
int x = arr[0];同样是把那个槽里的int值复制给x,后续改x不会影响arr[0]。
为什么修改元素能影响原数组?不是和“值传递”矛盾吗?
不矛盾。这里容易混淆的点在于把“传递方式”和“作用位置”混为一谈:
- 方法外定义
int[] a = {1, 2, 3};,a是一个引用,指向堆中一块连续内存(存着三个int值); - 调用
change(a),传入的是这个引用的副本,但它仍指向同一块堆内存; - 在方法内执行
arr[0] = 999;,实际是直接往那块共享的堆内存的第一个位置写入新值 —— 写的是原始位置,不是副本位置。
所以“能改到外面”,靠的是共享堆内存地址,而不是“传递了引用类型所以自动可变”。本质上,每个 arr[i] 的读写,对基本类型来说,仍是值的载入与存储。
对比:如果数组里存的是包装类(如 Integer),会怎样?
这能反向印证本质:
Integer[] nums = {new Integer(1), new Integer(2)};- 传入方法后,
nums[0] = new Integer(999);—— 这是把新对象的引用赋给数组的第 0 个槽; - 因为
Integer是不可变对象,你无法像改int那样“就地修改值”,只能换引用; - 而这个赋值操作,改的仍然是数组对象内部那个槽位的引用值(也是按值传递的逻辑)。
可见,无论元素是基本类型还是引用类型,数组内部的每个槽位存储的都是“值”:基本类型的值,或引用类型的地址值。所有对 arr[i] 的操作,底层都是对该槽位进行值的读/写。
一句话总结
数组作为参数,传递的是数组对象引用的副本;而数组中基本数据类型元素的“按值传递”,体现在每次对 arr[i] 的读取或赋值,都是对堆中某固定内存位置上的值做拷贝操作 —— 它不改变引用关系,只改变那个位置存的数;正因为位置是共享的,所以改了就立刻对外可见。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











