克隆就是复制一个对象的副本,但一个对象中有可能有基本数据类型,如 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接口


本文转载:CSDN博客