Java中ArrayList可跨类修改值而普通对象不行的原因及实现方案
Java跨类传参修改行为差异底层原理
Java所有方法传参的底层规则统一是值传递:不管传的是基本类型还是对象类型,传递的永远是参数值的拷贝,不存在直接传变量本身地址的引用传递。两种测试场景的行为差异,本质是传参后执行的操作性质完全不同。
两种场景的行为对比
1. ArrayList传参能修改成功的原因
- 调用
oa.fill(a)时,本质是把B类成员变量a中存储的「空ArrayList的堆内存地址值」拷贝一份,传给fill方法的List<Test>类型形参。此时方法形参和B类的成员变量a,共同指向堆里同一个空的ArrayList对象。 - fill方法内执行的
list.add(this)操作,是对这个共同指向的ArrayList对象做内部内容修改(往对象的存储数组里加元素),全程没有修改形参自己存的地址值。 - 因为形参和外部成员变量指向同一个被修改过的ArrayList,所以从外部看就是“传进去的集合被成功填充了”。
2. 普通自定义对象传参无法修改的原因
- 调用
oa.fill(a)时,同样是把B类成员变量a中存储的「null地址值」拷贝一份,传给fill方法的Test类型形参。此时方法形参和B类的成员变量a,共同指向null。 - fill方法内执行的
形参 = this操作,是直接修改方法局部形参自己存的地址值,让形参转而指向当前oa实例。这个操作既没有对原来共同指向的null做任何修改,也没有改动B类成员变量a自己存的地址值。 - 因为B类的成员变量
a从头到尾存的都是最初的null值,没有被做过任何修改,所以最终读取时还是null。
核心差异总结:集合场景是「修改共享对象的内部状态」,这个修改是所有指向该对象的引用都能感知的;普通对象测试场景是「修改局部形参自己存的引用值」,这个修改只在方法内部生效,完全不会影响外部的原变量。
普通对象实现同集合一样跨类修改效果的可行方案
只要脱离“直接给形参赋值”的操作逻辑,改成对共享对象的内部状态做修改,就能实现和ArrayList一样的跨类传参修改效果,常见实现方式:
- 自定义引用容器类:定义一个简单的持有目标对象的包装类,类内存一个可读写的成员变量。传参时传递这个包装类的引用,方法内修改包装类内部存的成员值,外部通过包装类就能读到修改后的值,原理和ArrayList内部存元素的逻辑完全一致。
- 用单元素数组做临时容器:传递一个长度为1的目标类型数组,方法内给数组的0号索引位置赋值,外部读取数组0位置的元素即可拿到修改后的结果。
- 通过持有外部类引用直接操作属性:把目标类的对象引用作为参数传入方法,方法内直接调用目标类的setter方法给对应成员变量赋值,本质是拿到了外部对象的引用后,直接修改外部对象的内部状态,而不是修改形参本身的引用。
- 使用JDK自带的
AtomicReference类做引用包装,和自定义容器类的效果完全一致。
内容的提问来源于stack exchange,提问作者Ngô Đức Bùi Anh Tuấn
相关产品推荐
相关产品推荐

