JavaSE第一百零五讲:对象的深克隆与浅克隆(这里面两个例子有争议)

    在前面的几讲解内容中,我们学习了线程的一些知识,现在我们在回过头去继续学习I/O的内容,主要是对象的深克隆和浅克隆,也叫对象的深赋值和浅赋值。这也是比较重要的内容。

1. 深拷贝(deep clone) 与 浅拷贝(shallow clone)

浅复制(浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。如图105-1所示

JavaSE第一百零五讲:对象的深克隆与浅克隆(这里面两个例子有争议)_第1张图片

                    图105-1:浅复制


深复制(深克隆):被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的目标对象所引用的对象都复制了一遍。图105-2:深复制


JavaSE第一百零五讲:对象的深克隆与浅克隆(这里面两个例子有争议)_第2张图片

                         图105-2:深复制


2. Java的clone()方法【定义在Object类中】,查看JDK 文档中Object类里面的clone()方法。

1) clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:

① 对任何的对象x,都有x.clone() !=x  [克隆对象与原对象不是同一个对象]

② 对任何的对象x,都有x.clone().getClass()= =x.getClass()  [克隆对象与原对象的类型一样]

③ 如果对象x的equals()方法定义恰当,良好(就是没有去改写equals()方法),那么x.clone().equals(x)应该成立。

2) Java中如何进行对象的克隆

① 为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。

② 在派生类中覆盖基类的clone()方法,并声明为 public [Object类中的clone()方法为protected的]。(注意这边必须声明为public,不然其他包中就没法对其进行引用了。)

③ 在派生类的clone()方法中,调用super.clone()。[调用父类的clone()方法主要让父类去完成一些特定类型识别的工作,然后重写的子类可以去完成特定当前子类的拷贝动作。]

在派生类中实现Cloneable接口。

查看JDK文档中Cloneable接口,可以发现它里面没有定义任何方法,与Serializable接口定义一样,说明它是一个标示性的接口。编译器看到之后,就知道这种约定,没有去实现这种方法。

3. 说明

① 为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?

  因为在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。

继承自java.lang.Object类的clone()方法是浅复制。[也就是如果使用clone()进行对象的克隆的话,我们仅仅是使用了浅克隆(浅复制)而没有真正实现深克隆(深复制)]

4. 程序demo:浅复制

/*
 * 浅克隆
 */
public class CloneTest1 {
    public static void main(String[] args) throws Exception {
        Student student = new Student();
        student.setAge(20);
        student.setName("zhangsan");
        
        Student student2 = (Student)student.clone();
        System.out.println(student2.getAge());
        System.out.println(student2.getName());
    }
}
class Student implements Cloneable{
    
    private int age;
    private String name;
    
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        //这边克隆的是Student的这个对象
        Object object = super.clone();
        return object;
    }
    
}
编译执行结果:

20
zhangsan

【说明】:克隆对象成功,现在继续对上面程序的main()方法进行修改,修改如下:

        Student student = new Student();
        student.setAge(20);
        student.setName("zhangsan");
        
        Student student2 = (Student)student.clone();
        System.out.println(student2.getAge());
        System.out.println(student2.getName());
        System.out.println("------------------------");
        
        student2.setName("lisi");
        System.out.println(student.getName());
        System.out.println(student2.getName());
编译执行结果:

20
zhangsan
------------------------
zhangsan
lisi

【说明】:Object类的clone()是属于浅复制,程序状况如下图所示:

JavaSE第一百零五讲:对象的深克隆与浅克隆(这里面两个例子有争议)_第3张图片

从这边也可以总结出,student 与 student1 不是同一个对象。

2. 继续写一个复杂的程序,程序如下所示:

public class CloneTest2 {
    public static void main(String[] args) throws Exception {
        
        Teacher teacher = new Teacher();
        
        teacher.setAge(40);
        teacher.setName("Teacher zhang");
        
        Student2 s1 = new Student2();
        s1.setAge(20);
        s1.setName("zhangsan");
        s1.setTeacher(teacher);
        
        Student2 s2 = (Student2)s1.clone();
        System.out.println(s2.getName());
        System.out.println(s2.getAge());
        System.out.println(s2.getTeacher().getAge());
        System.out.println(s2.getTeacher().getName());
        
        
    }
}

class Teacher implements Cloneable{
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

class Student2 implements Cloneable{
    private int age;
    private String name;
    private Teacher teacher;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
    /*
     * 复制student2对象
     * 重写的子类的访问修饰符可比父类的要大。
     */
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        Object object = super.clone();
        return object;
    }
}
编译执行结果:

zhangsan
20
40
Teacher zhang

继续修改上述程序中的main()方法的内容

    public static void main(String[] args) throws Exception {
        
        Teacher teacher = new Teacher();
        
        teacher.setAge(40);
        teacher.setName("Teacher zhang");
        
        Student2 s1 = new Student2();
        s1.setAge(20);
        s1.setName("zhangsan");
        s1.setTeacher(teacher);
        
        
        Student2 s2 = (Student2)s1.clone();
        System.out.println(s2.getName());
        System.out.println(s2.getAge());
        
        teacher.setName("Teacher Li");
        
        System.out.println(s2.getTeacher().getAge());
        System.out.println(s2.getTeacher().getName());
        
        
    }
编译执行结果:

zhangsan
20
40
Teacher Li

【说明】:究其原因是因为clone这个对象是浅复制的概念,被引用的对象是没有进行复制的,只有原生数据类型才会进行真正的复制。


3.修改这个程序,使得它可以进行深复制

单纯的复制Student肯定是有不行,我们必须复制了Student之后,还要继续复制Teacher里面的内容

public class CloneTest2 {
    public static void main(String[] args) throws Exception {
        
        Teacher teacher = new Teacher();
        
        teacher.setAge(40);
        teacher.setName("Teacher zhang");
        
        Student2 s1 = new Student2();
        s1.setAge(20);
        s1.setName("zhangsan");
        s1.setTeacher(teacher);
        
        
        Student2 s2 = (Student2)s1.clone();
        System.out.println(s2.getName());
        System.out.println(s2.getAge());
        
        teacher.setName("Teacher Li");
        
        System.out.println(s2.getTeacher().getAge());
        System.out.println(s2.getTeacher().getName());
        
        
    }
}

class Teacher implements Cloneable{
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    //赋值Teacher当前的两个对象,age和name
    @Override
    public Object clone() throws CloneNotSupportedException {
            return super.clone();
    }
}

class Student2 implements Cloneable{
    private int age;
    private String name;
    private Teacher teacher;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
    /*
     * 复制student2对象
     * 重写的子类的访问修饰符可比父类的要大。
     */
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        Student2 student2 = (Student2)super.clone();
        //再复制一个Teacher
        student2.setTeacher((Teacher)student2.getTeacher().clone());
        return student2;
    }
}
编译执行结果:

zhangsan
20
40
Teacher zhang

【说明】:




你可能感兴趣的:(java,java,java,对象的克隆)