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的引用类型来说

JAVA的深克隆 java对象深克隆_深克隆

可以看的出来,String其实指向的是同一个地址,只是由于String类自身的特殊性,我们不要再重写它的clone方法


2.2 除String外的其他引用类型

和String类型不同,其他引用类型是可变的,所以我们再用clone进行拷贝时,就会造成对象值之间的相互影响

拿1中的程序举例,如果我们Student中不进行clone方法的重写,Person类中也不对Student类进行特殊处理,那么图解应该是这样的

:Person类和Student类,我没有String类型的属性,是不想让大家看图误解,实际也可以把它看作是“深克隆”(不严谨),即和基本数据类型相同

JAVA的深克隆 java对象深克隆_后端_02

此时,我们只要修改p2中Student类中的属性值,那么p1中Student类中的属性值也会受到影响

而,当我们将Student类也进行clone重写后,Student类克隆的图解为

JAVA的深克隆 java对象深克隆_java_03

即Student对象之间不会进行相互影响

因此,我们Person类进行克隆时的图解就可以得到

JAVA的深克隆 java对象深克隆_深克隆_04

即,我们可以想象该图就是

JAVA的深克隆 java对象深克隆_基本数据类型_05

注:红色部分就是想象出来的,即是Student中的属性。现在p1和p2就不存在交集,他们的值也不会互相影响


2.3 当出现引用嵌套时

即Person中存在Student类型,而Student中又存在另一个School类型,那么要实现深克隆,则School也需要重写clone,同时在Student中对School进行特殊处理,否则它还不是一个真正的深克隆

  • 如果只处理Student,不处理School,浅克隆

JAVA的深克隆 java对象深克隆_深克隆_06

  • Student,School都进行处理,深克隆

JAVA的深克隆 java对象深克隆_后端_07