1. 写在前面
今天遇到了这样一个问题,事实上这个问题是之前遇到过的。
java 中列表的赋值的问题。
这个问题核心是 deep copy
& shallow copy
的问题
2. 情景再现
public class MikeTest {
public static void main(String[] args) throws NoSuchMethodException {
class Person{
private String name;
private Integer age;
Person(String name, Integer age){
this.name = name;
this.age = age;
}
}
// TODO: 2022/3/8 测试一下这个 bug
List<Person> rawList = new ArrayList<>();
Person person1 = new Person("mike",24);
Person person2 = new Person("John",28);
rawList.add(person1);
rawList.add(person2);
List<Person> updatedList = rawList;
for (Person person : updatedList){
person.age = 90;
}
System.out.println(updatedList);
System.out.println(rawList);
}
}
上述代码运行之后,问题如下:
bug结果
3. 问题原因
List<Person> updatedList = rawList;
这一句代码,事实上是将指针给过来了,因此两个 List 对应的是同样的内容
update 之后,新的变了,原来的也变了(因为是一个)
4. 怎么解
一开始我以为下面的方式就可以解决(因为大一学c++课的时候,记得有这么讲过)
List<Person> updatedList = new ArrayList(rawList);
但是最后搞完了,发现问题还是没有解决
5. deep copy
后续去查了一下,这里其实是 shallow copy
& deep copy
的问题。
shallow copy
这就是在上述 3
部分中所描述的,shallow copy
只会将地址指到相同的地方,不会重新创建对象。
deep copy
deep copy
就是要一份新的引用对象。
实现则需要元素类去实现 Cloneable
接口, @Override
其中的 clone()
方法。
看代码
/**
* 实现 Cloneable 的类 对象是人
* 完成深拷贝
* 事实上,为了完成深拷贝,是需要被拷贝的元素具有这样的能力
*
* @author mikeshine
*/
@Data
public class Person implements Cloneable{
/**
* 名字
*/
private String name;
/**
* 年龄
*/
private Integer age;
Person(String name, Integer age){
this.age = age;
this.name = name;
}
/**
* clone 方法的核心目的就是 返回一个开辟新地址空间的 对象
*
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Person clone() throws CloneNotSupportedException {
// 首先给新对象开辟一个地址空间
Person newPerson = (Person)super.clone();
// 然后依次将属性 copy
newPerson.setName(this.name);
newPerson.setAge(this.age);
return newPerson;
}
}
使用如下
List<Person> rawList = new ArrayList<>();
Person person1 = new Person("mike",24);
Person person2 = new Person("John",28);
rawList.add(person1);
rawList.add(person2);
List<Person> updatedList = new ArrayList<>();
for(Person person : rawList){
// 这里是重点,相当于用 clone() 方法重新生成一个新对象放在新地址
updatedList.add(person.clone());
}
网友评论