Java中使用clone实现深克隆
- 首先,需要了解clone方法实际上实现的是浅克隆,即会创建一个新的对象,如果对象中的属性是基本数据类型则拷贝值,如果是引用数据类型则拷贝内存地址,也就意味着任何一个对象改变都会对其他产生影响。
- 通过今天的学习,了解到Java中可以通过clone实现深克隆,即克隆出来的对象不会受到其他对象值的影响,它可以看作是一个崭新的对象。
- 虽然方法比较容易记住,但是不了解其中的原理,查询了很多博客总结了一部分。
1. 实现方法
先将实现方法列出,再进行讲解
- Person类:属性中存在另一个其他引用类型,需要重写clone方法
public class Person implements Cloneable {
private Integer age;
private Student student;
public Person() {
}
public Person(Integer age, Student student) {
this.age = age;
this.student = student;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", student=" + student +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.student = (Student) student.clone();
return person;
}
}
- Student类:需要重写clone
public class Student implements Cloneable{
private int id;
public Student() {
}
public Student(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- Test类:测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student(1001);
Person p1 = new Person(20,s1);
Person p2 = (Person) p1.clone();
p2.setAge(30);
System.out.println(p1);//Person{age=20, student=Student{id=1001}}
System.out.println(p2);//Person{age=30, student=Student{id=1001}}
}
}
显然对p2的属性进行修改,没有影响到p1,说明深克隆成功
2. 实现原理
2.1 基本数据类型和String
- 首先,对于基本数据类型,对于他们的拷贝没有太多疑问,直接将值拷贝过来,不会相互影响,对于一个引用只存在基本数据类型的情况下,我们也可以认为它是一种“深克隆”
- 对于String类型,它是一种引用类型,但是它的特殊性在于,它是一个不可改变的类,String类中的函数不能更改自身的值,所以表面上String类型和基本类型一样,好像是实现了“深克隆”,其实String类的地址还是相同的,指向的都是一个地址,只是由于无法改变值的特性,给我们一种“深克隆”的假象。
- 对于只包含基本数据类型和String的引用类型来说
可以看的出来,String其实指向的是同一个地址,只是由于String类自身的特殊性,我们不要再重写它的clone方法
2.2 除String外的其他引用类型
和String类型不同,其他引用类型是可变的,所以我们再用clone进行拷贝时,就会造成对象值之间的相互影响
拿1中的程序举例,如果我们Student中不进行clone方法的重写,Person类中也不对Student类进行特殊处理,那么图解应该是这样的
注:Person类和Student类,我没有String类型的属性,是不想让大家看图误解,实际也可以把它看作是“深克隆”(不严谨),即和基本数据类型相同
此时,我们只要修改p2中Student类中的属性值,那么p1中Student类中的属性值也会受到影响
而,当我们将Student类也进行clone重写后,Student类克隆的图解为:
即Student对象之间不会进行相互影响
因此,我们Person类进行克隆时的图解就可以得到
即,我们可以想象该图就是
注:红色部分就是想象出来的,即是Student中的属性。现在p1和p2就不存在交集,他们的值也不会互相影响
2.3 当出现引用嵌套时
即Person中存在Student类型,而Student中又存在另一个School类型,那么要实现深克隆,则School也需要重写clone,同时在Student中对School进行特殊处理,否则它还不是一个真正的深克隆
- 如果只处理Student,不处理School,浅克隆
- Student,School都进行处理,深克隆