Java之浅克隆和深克隆详解编程语言

克隆就是复制一个对象的副本,但一个对象中有可能有基本数据类型,如 int,long,float等,也可能含有对象引用数据类型,如 数组,集合等

浅克隆:

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

深克隆:

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

克隆方法clone()

clone是定义一个Object类下基本方法之一,任何克隆的过程都将实现clone()方法,而其在Object接口中定义如下:

protected native Object clone() throws CloneNotSupportedException;

通常克隆对象都是通过调用super.clone()方法来获取克隆对象的,所以任何克隆的过程最终都将到达java.lang.Object的clone()方法。但是在覆写clone()方法时,这个类需要实现Clonable接口,这个接口中没有定义方法,只做为一种标识存在。如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了super.Clone() 方法),那么Object的clone()方法就会抛出CloneNotSupportedException 异常。

例子:

Student.java

public class Student implements Cloneable { 
 
    private String name; 
    private int age; 
    private Teacher teacher; 
 
    public Student(String name, int age, Teacher teacher) { 
        this.name = name; 
        this.age = age; 
        this.teacher = teacher; 
    } 
 
    /** 
     *  省略 setX、getX 方法 
     */ 
}

Teacher.java

public class Teacher implements Cloneable { 
 
    private String name; 
    private String course; 
 
    public Teacher(String name, String course) { 
        this.name = name; 
        this.course = course; 
    } 
 
    /** 
     *  省略 setX、getX 方法 
     */ 
}

Student类中包含有name,age属性和Teacher对象

浅克隆实现:

克隆对象实现Cloneable接口,在克隆的方法里面调用super.clone(),就会返回克隆后的对象。

public class Student implements Cloneable { 
 
    private String name; 
    private int age; 
    private Teacher teacher; 
 
    public Student(String name, int age, Teacher teacher) { 
        this.name = name; 
        this.age = age; 
        this.teacher = teacher; 
    } 
 
    /** 
     * 实现clone方法 
     */ 
    public Student clone() { 
        Student stu = null; 
        try { 
            stu = (Student) super.clone(); 
        } catch (CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        return stu; 
    } 
}

测试:

public class Student implements Cloneable { 
 
    private String name; 
    private int age; 
    private Teacher teacher; 
 
    public Student(String name, int age, Teacher teacher) { 
        this.name = name; 
        this.age = age; 
        this.teacher = teacher; 
    } 
 
    /** 
     * 实现clone方法 
     */ 
    public Student clone() { 
        Student stu = null; 
        try { 
            stu = (Student) super.clone(); 
        } catch (CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        return stu; 
    } 
 
    public static void main(String[] args) { 
        Teacher teacher = new Teacher("胡老师", "100"); 
        Student student = new Student("张三", 27, teacher); 
 
        Student stu2 = student.clone(); 
        stu2.setName("李四"); 
        stu2.setAge(28); 
        stu2.getTeacher().setName("刘老师"); 
 
        System.out.println("克隆前:" + student.getName() + "," + student.getAge() + "," 
                + student.getTeacher().getName()); 
        System.out.println("克隆后:" + stu2.getName() + "," + stu2.getAge() + "," 
                + stu2.getTeacher().getName()); 
 
        System.out.println(student.getTeacher() == stu2.getTeacher()); 
    } 
 
    public String getName() { 
        return name; 
    } 
 
    public void setName(String name) { 
        this.name = name; 
    } 
 
    public int getAge() { 
        return age; 
    } 
 
    public void setAge(int age) { 
        this.age = age; 
    } 
 
    public Teacher getTeacher() { 
        return teacher; 
    } 
 
    public void setTeacher(Teacher teacher) { 
        this.teacher = teacher; 
    } 
 
}

输出:

克隆前:张三,27,刘老师 
克隆后:李四,28,刘老师 
true

从上面结果可知,克隆出来的Student对象里的name和age是新的,但teacher还是之前的,这就是浅克隆

深克隆实现

Student.java

public class Student implements Cloneable { 
 
    private String name; 
    private int age; 
    private Teacher teacher; 
 
    public Student(String name, int age, Teacher teacher) { 
        this.name = name; 
        this.age = age; 
        this.teacher = teacher; 
    } 
 
    /** 
     * 实现clone方法 
     */ 
    public Student clone() { 
        Student student = null; 
        try { 
            student = (Student) super.clone(); 
            Teacher teacher = this.teacher.clone(); 
            student.setTeacher(teacher); 
        } catch (CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        return student; 
 
    } 
 
    /** 
     *  省略 setX、getX 方法 
     */ 
 
}

Teacher.java

public class Teacher implements Cloneable { 
 
    private String name; 
    private String course; 
 
    public Teacher(String name, String course) { 
        this.name = name; 
        this.course = course; 
    } 
 
    public Teacher clone() { 
 
        Teacher clone = null; 
        try { 
            clone = (Teacher) super.clone(); 
        } catch (CloneNotSupportedException e) { 
            e.printStackTrace(); 
        } 
        return clone; 
    } 
 
    /** 
     *  省略 setX、getX 方法 
     */ 
 
}

要实现深克隆的话,克隆对象里面的对象类型也必须实现Cloneable接口并调用clone()。

测试

public static void main(String[] args) { 
    Teacher teacher = new Teacher("胡老师", "100"); 
    Student student = new Student("张三", 27, teacher); 
 
    Student stu2 = student.clone(); 
    stu2.setName("李四"); 
    stu2.setAge(28); 
    stu2.getTeacher().setName("刘老师"); 
 
    System.out.println("克隆前:" + student.getName() + "," + student.getAge() + "," 
            + student.getTeacher().getName()); 
    System.out.println("克隆后:" + stu2.getName() + "," + stu2.getAge() + "," 
            + stu2.getTeacher().getName()); 
 
    System.out.println(student.getTeacher() == stu2.getTeacher()); 
}

输出结果:

克隆前:张三,27,胡老师 
克隆后:李四,28,刘老师 
false

这时,两个对象的中的Teacher就不是同一个对象了,实现了深克隆,但是如果要克隆的对象继承链比较长的话要实现深克隆,就必须逐层地实现Cloneable,这个过程是比较麻烦的,不过还有一种方法可以简便地实现深克隆。

serializable克隆

大家知道,Java可以把对象序列化写进一个流里面,反之也可以把对象从序列化流里面读取出来,但这一进一出,这个对象就不再是原来的对象了,就达到了克隆的要求。

public class Student implements Serializable { 
 
    private String name; 
    private int age; 
    private Teacher teacher; 
 
    public Student(String name, int age, Teacher teacher) { 
        this.name = name; 
        this.age = age; 
        this.teacher = teacher; 
    } 
 
    public Student serializableClone() throws IOException, 
            ClassNotFoundException { 
        Student clone; 
 
        ByteArrayOutputStream bo = new ByteArrayOutputStream(); 
        ObjectOutputStream oo = new ObjectOutputStream(bo); 
        oo.writeObject(this); 
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); 
        ObjectInputStream oi = new ObjectInputStream(bi); 
        clone = (Student) oi.readObject(); 
 
        return clone; 
    } 
 
    public static void main(String[] args) throws IOException, ClassNotFoundException { 
        Teacher teacher = new Teacher("胡老师", "100"); 
        Student student = new Student("张三", 27, teacher); 
 
        Student stu2 = student.serializableClone(); 
        stu2.setName("李四"); 
        stu2.setAge(28); 
        stu2.getTeacher().setName("刘老师"); 
 
        System.out.println("克隆前:" + student.getName() + "," + student.getAge() 
                + "," + student.getTeacher().getName()); 
        System.out.println("克隆后:" + stu2.getName() + "," + stu2.getAge() + "," 
                + stu2.getTeacher().getName()); 
 
        System.out.println(student.getTeacher() == stu2.getTeacher()); 
    } 
 
    /** 
     *  省略 setX、getX 方法 
     */ 
 
}

Teacher.java也要实现Serializable接口

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/14187.html

(0)
上一篇 2021年7月19日 16:38
下一篇 2021年7月19日 16:38

相关推荐

发表回复

登录后才能评论