1. 问题引入
前两天一同事问了我一个问题大致是 List newList = new ArrayList<对象>(list); 然后对newList中的对象进行了一系列的操作,之后回头看 list 对象的时候,里面相同的值竟然也被修改了。
2. 问题复现及分析
2.1 newArrayList<基本类型或String>() ,下述基本对象为 String (此处没有问题)
2.2 newArrayList<对象>() ,下述基本对象 (此处有问题)
依据以往使用list 的经验, 第二张图的第二行应该输出张三而不是王五,然后同事就问我是为啥。 为啥呢我说这不是 吃了有经验的亏(基本类型或String是没问题的)[狗头] ,其实有的小伙伴可能已经知道了这是对象 copy问题
2.2 浅copy 和 深copy
浅copy是指对象复制的时候,简单类型会直接copy值,对象类型的数据只copy指针引用,这个对象里面的属性不会copy;
深copy是指对象复制的时候,简单类型会直接copy值,对象类型的数据会copy新对象;
java默认的copy方式是浅copy,也就是说上述二截图中的Student 不管你newArray<Student>(studentList); 不管你 new多少次,实际上 student只有一个,所以你改动任意一个则都会改动
3. 解决方案
3.1 原生 clone 方法(不推荐)
两大步骤,实现 Cloneable 重写 clone实现,但是这种方式比较麻烦,不仅要自己实现,而且当类中还有类的时候依旧需要重复上述操作。由于ArrayList 属性情况特殊,此种方式不太可行,用另一种方式来说明:
1.Student 类
public class Student implements Cloneable {
private String name;
private Integer age;
// 学科
private Discipline discipline;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Discipline getDiscipline() {
return discipline;
}
public void setDiscipline(Discipline discipline) {
this.discipline = discipline;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Student{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append(", discipline=").append(discipline);
sb.append('}');
return sb.toString();
}
@Override
protected Object clone() {
// 原生clone
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
// 重新后
// Student str = null;
// try {
// str = (Student) super.clone();
// Discipline cloneDiscipline = (Discipline) discipline.clone();
// str.discipline = cloneDiscipline;
// return str;
// } catch (CloneNotSupportedException e) {
// e.printStackTrace();
// }
// return str;
}
2. Discipline 学科类
public class Discipline implements Cloneable {
private String name;
public String getName() {
return name;
}
public Discipline setName(String name) {
this.name = name;
return this;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Discipline{");
sb.append("name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
3.测试方法
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
student.setDiscipline(new Discipline().setName("语文"));
System.out.println("原student: " + student);
Student student1 = (Student) student.clone();
student1.getDiscipline().setName("数学");
System.out.println("修改后student: " + student);
System.out.println("student1:" + student1);
}
4. 原生clone 输出结果
5.修改后的clone方法的执行结果
3.2 借助序列化(推荐)
3.2.1 可以借助三方工具包来实现 比如fastjson 、 jackson 等
先导包 再实现
// 导包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
// 实现 对象方式
String studentStr = JSONObject.toJSONString(student);
student = JSONObject.parseObject(studentStr, Student.class);
// 实现数组方式
String studentListStr = JSONArray.toJSONString(studentList);
List<Student> students = JSONArray.parseArray(studentListStr, Student.class);
3.2.2 自己实现(不建议)
自己实现借助io流 比较费时不建议使用
4. 总结
ArrayList<对象>(Collection<? extends E> c) 是浅copy,泛型是对象的时候只copy引用;
深copy常用方式,实现 Cloneable 重写clone接口;借助三方序列化工具;