Java对象数组传值疑惑:为何数组元素可修改而字符串对象不行?
嘿,这个问题刚好戳中了Java值传递里特别容易混淆的点,尤其是有C语言背景的话,确实会因为习惯了数组名和首元素地址的关联而绕进去!我来帮你理清楚:
首先得再明确一遍:Java里所有参数传递都是按值传递——不管是基本类型还是引用类型,传递的都是参数的「值」。对于引用类型来说,这个「值」就是对象的内存地址(引用的副本)。
先看String参数的例子
public static void changeTheName(String obj){ obj.toUpperCase(); }
这里传递的是obj引用的副本,toUpperCase()会返回一个新的String对象,但我们并没有把这个新对象赋值给任何变量(包括方法里的副本obj)。加上String本身是不可变的,原引用指向的旧String对象完全没被改动,所以外部看不到变化,这部分你理解的没错。
再看String数组修改元素的情况
public static void changeTheName(String obj[]){ for(int i=0;i<obj.length;i++) obj[i]=obj[i].toUpperCase(); }
这里的关键是:方法里的obj是原数组引用的副本,它和原引用指向的是同一个数组对象。而obj[i]是这个数组对象内部存储的元素(每个元素都是一个String引用)。
当我们执行obj[i] = obj[i].toUpperCase()时,我们并没有修改副本引用obj本身,而是修改了它指向的数组对象里的元素——把数组里的每个String引用替换成了新的大写String对象。因为原引用和副本引用指向同一个数组,所以外部能看到数组元素的变化。
为什么重新赋值数组没用?
如果在方法里写:
public static void changeTheName(String obj[]){ obj = new String[]{}; }
这时候我们是把副本引用obj指向了一个全新的数组对象,但原引用还是指向原来的旧数组。因为我们修改的是副本引用本身,而不是它指向的对象的内部数据,所以外部完全看不到这个变化——这和第一个String例子里没把新对象赋值回去是一个逻辑。
你提到的核心关键点:obj和obj[0]的引用是独立的
这正是问题的核心!在C语言里,数组名和首元素的地址有特殊的等价关系,但Java里数组是实实在在的对象:
obj是指向整个数组对象的引用obj[0]是数组对象内部存储的一个引用值,它指向某个String对象
这两个引用完全独立,修改obj[0]只是修改数组对象里的一个元素,不会影响obj本身指向的数组;而修改obj(让它指向新数组)也不会影响原数组里的元素。
总结一下:
- Java永远是按值传递,引用类型传递的是引用的副本
- 如果修改的是副本引用指向的对象的内部数据,原引用能看到变化(比如数组元素)
- 如果修改的是副本引用本身(让它指向新对象),原引用不受影响(比如重新赋值数组、给String参数赋值新对象却不返回)
内容的提问来源于stack exchange,提问作者Rose

