深克隆和浅克隆

为什么需要克隆?

new出来的对象中的属性都是初始化时候的值,当需要一个新的对象来保存当前对象的“状态”就靠clone方法了。直接使用Object a=new Object();Object b;b=a不行吗,答案是不行的。因为这只是克隆了引用。

如何实现克隆?

首先由两种克隆的方式,一个是深克隆,一个是浅克隆。他们之间的区别在于:是否支持引用类型的成员变量的复制,深克隆会克隆引用类型的成员变量。

clone方法来源于Object,我们追本逐源看看:

 protected native Object clone() throws CloneNotSupportedException;

可以看出,clone()Objectnative方法,而且还是protected以为着不能再类外进行访问,想要实现克隆方法,只有覆盖这个方法。

public class Student implements Cloneable {
    private String name;
    private int age;
    
    @Override
    protected Object clone()  {
        Student student=null;
        try {
            student =(Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;

    }
   ...
   getter and setter
   ...
public class Test {
    public static void main(String[] args){
        Student stu1 = new Student("张三",18);
        Student stu2 = (Student) stu1.clone();
        
        stu2.setName("李四");
        stu2.setAge(20);
        System.out.println("stu1:"+stu1.getName()+" age:"+stu1.getAge());
        System.out.println("stu2:"+stu2.getName()+" age:"+stu2.getAge());
        System.out.println(stu2 == stu1);  //false
    }
}
stu1:张三 age:18
stu2:李四 age:20
false

上面的案例,属于浅克隆,克隆对象中的属性不包括引用对象,如果包含引用对象,则该引用对象也必须要实现cloneable接口。

public class Student implements Cloneable {
    private String name;
    private int age;
    private Address address;        //新增地址对象
    
    @Override
    protected Student clone()  {
        Student stu =null;
        try {
            stu = (Student) super.clone();
            stu.setAddress(address.clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }
    ....
public class Address implements Cloneable {
    private String add;
    
    //也需要实现clone方法
    @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address)super.clone();
    }
    ....
}
public class Test {
    public static void main(String[] args){
        Student stu1 = new Student("张三",18,new Address("上海"));
        Student stu2 = (Student) stu1.clone();

        stu2.setName("李四");
        stu2.setAge(20);
        stu1.setAddress(new Address("北京"));
        System.out.println("stu1:"+stu1.getName()+" age:"+stu1.getAge()+"address"+stu1.getAddress().getAdd());
        System.out.println("stu2:"+stu2.getName()+" age:"+stu2.getAge()+"address"+stu2.getAddress().getAdd());
    }
}
stu1:张三 age:18address北京
stu2:李四 age:20address上海

如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。

public class Student implements Serializable {
    private static final long serialVersionUID = -3950409490183038226L;

    private String name;
    private int age;
    private Address address;
    
    public Student CloneObject(Object obj){
        Student student = null;
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
             student = (Student) ois.readObject();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return student;
    }
......
public class Address implements Serializable {

    private static final long serialVersionUID = -4265312082191071263L;
    private String add;

    @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address)super.clone();
    }
    .....
}
public class Test {
    public static void main(String[] args){
        Student stu1 = new Student("张三",18,new Address("上海"));
        Student stu2 = stu1.CloneObject(stu1);

        stu2.setName("李四");
        stu2.setAge(20);
        stu1.setAddress(new Address("北京"));
        System.out.println("stu1:"+stu1.getName()+" age:"+stu1.getAge()+"address"+stu1.getAddress().getAdd());
        System.out.println("stu2:"+stu2.getName()+" age:"+stu2.getAge()+"address"+stu2.getAddress().getAdd());
    }
}
stu1:张三 age:18address北京
stu2:李四 age:20address上海

总结:

实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;

2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆.

注意:

基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

你可能感兴趣的:(深克隆,浅克隆,clone,java基础)