为何在Getter中使用Arrays.copyOf()实现的是防御性拷贝而非浅拷贝?
我是Java编程初学者,对在Getter方法中使用Arrays.copyOf()的结果感到困惑。我知道该方法通常会生成传入参数的浅拷贝,但在Getter中用它实现数组的防御性拷贝时,返回的结果似乎并非浅拷贝。
PersonRecord类代码
import java.util.Arrays; public class PersonRecord{ private final String name; private final String dob; private final PersonRecord[] kids; // 构造方法 public PersonRecord(String name, String dob, PersonRecord[] kids) { this.name = name; this.dob = dob; this.kids = kids; } // 字段"kids"的Getter方法 // 对第三个字段使用防御性拷贝 public PersonRecord[] getKids() { return Arrays.copyOf(kids, kids.length); } // toString()方法(仅打印"kids"字段的内容) @Override public String toString(){ String kidString = "n/a"; String[] names = new String[kids.length]; Arrays.setAll(names, i -> names[i] = kids[i].name); kidString = String.join(",", names); return kidString; } }
测试代码
// 创建第三个字段"johnKids"的内容 PersonRecord jane = new PersonRecord("Jane", "01/01/1930", null); PersonRecord jim = new PersonRecord("Jim", "02/02/1932", null); PersonRecord joe = new PersonRecord("Joe", "03/03/1934", null); // 为第三个字段创建数组 PersonRecord[] johnsKids = {jane, jim, joe}; // 创建类的实例"john" PersonRecord john = new PersonRecord("John", "05/05/1900", johnsKids); // 打印对象john System.out.println(john); // 结果:第三个字段 => {Jane, Jim, Joe} // 将Getter方法返回的结果赋值给新数组 PersonRecord[] newArray = john.getKids(); // 对新数组做一些修改 newArray[0] = jim; newArray[1] = new PersonRecord("Ann", "04/04/1936", null); // 再次打印对象john System.out.println(john); // 结果(未被修改):第三个字段 => {Jane, Jim, Joe}
测试结果表明newArray与原对象的kids字段指向不同内存对象。我疑惑的是:既然Arrays.copyOf()应返回浅拷贝,为何修改newArray不会影响原kids字段?
解答
首先得明确浅拷贝的定义:Arrays.copyOf()做的浅拷贝,是创建一个新的数组对象,然后把原数组中每个元素的引用,复制到新数组对应的位置里。也就是说,新数组和原数组是两个独立的数组对象,但它们里面的元素指向的是同一个堆内存中的对象。
你测试里修改的是newArray的元素引用(比如newArray[0] = jim),这只是改变了新数组里某个位置存储的引用值,和原数组没有任何关系——原数组的kids还是保留着原来的引用集合,所以打印john时内容不变。
如果要体现浅拷贝的特性,你可以试试修改newArray中某个元素的内部状态(前提是这个元素的字段不是final)。比如假设PersonRecord的name不是final,你执行newArray[0].name = "ModifiedJane",这时候再打印john,就会发现原kids数组里对应的元素名称也变成了"ModifiedJane"——因为新数组和原数组里的元素指向的是同一个PersonRecord对象。
而你现在用Arrays.copyOf()实现的防御性拷贝,目的就是防止外部直接修改原数组本身(比如替换数组里的元素、改变数组长度),这正是防御性拷贝想要达到的效果,和浅拷贝的定义并不冲突。
内容的提问来源于stack exchange,提问作者Bert Horimx

