java克隆

java克隆分为浅克隆和深克隆,概念如下:

浅拷贝(浅克隆)
克隆出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。

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

测试:

​ 接下来我们新建两个实体类,一个为Person类,另一个为Father类,同时实现Clonable接口

Person.java

public class Person implements Cloneable {
    private String name;
    private int age;
    private Father father;

    public Person(String name,int age,Father father){
        this.name = name;
        this.age = age;
        this.father = father;
    }

    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 Father getFather() {
        return father;
    }

    public void setFather(Father father) {
        this.father = father;
    }
}

Father.java

public class Father implements Cloneable {
    private String name;
    private int age;

    public Father(String name, int age){
        this.name = name;
        this.age = age;
    }

    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;
    }
}

注:这里不能使用lombok注解

测试一:

为Person重写clone方法:

public Person(String name,int age,Father father){
        this.name = name;
        this.age = age;
        this.father = father;
    }

编写测试方法:

Father father = new Father("小明父亲",32);
        Person person = new Person("小明",12,father);

        Person personCloneable = person.clone();
        personCloneable.setAge(1234);
        personCloneable.setName("小刚");
        personCloneable.getFather().setName("小刚父亲");
        System.out.println(person.getName()+":"+person.getAge());
        System.out.println(personCloneable.getName()+":" + personCloneable.getAge());
        System.out.println(person.getFather().getName()+":"+personCloneable.getFather().getName());
        System.out.println(person.getFather() == personCloneable.getFather());

输出结果:

小明:12
小刚:1234
小刚父亲:小刚父亲
true

从测试结果来看person对象的age和name属性成功被克隆,但father属性的引用未变化,因此这是一个浅克隆。

测试二

为Father类重写clone方法:

    @Override
    public Father clone() throws CloneNotSupportedException {
        Father father = null;
        father = (Father) super.clone();
        return father;
    }

为Person类重写clone方法:

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person person = null;
        Father father = this.father.clone();
        person = (Person) super.clone();
        person.setFather(father);
        return person;
    }

再次使用测试一中的测试方法,结果如下:

小明:12
小刚:1234
小明父亲:小刚父亲
false

可以发现此时被克隆的对象的father对象引用和原对象中的father引用不相同,此时我们实现了深克隆。

扩展

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

serializable克隆

PersonSer.java

 private String name;
    private int age;
    private FatherSer father;

    public PersonSer serializableClone() throws IOException, ClassNotFoundException {
        PersonSer person;

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(this);
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        person = (PersonSer) oi.readObject();
        return person;
    }

    public PersonSer(String name,int age,FatherSer father){
        this.name = name;
        this.age = age;
        this.father = father;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person person = null;
        Father father = this.father.clone();
        person = (Person) super.clone();
        person.setFather(father);
        return person;
    }

    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 FatherSer getFather() {
        return father;
    }

    public void setFather(FatherSer father) {
        this.father = father;
    }

FatherSer.java

public class FatherSer implements Serializable {
    private String name;
    private int age;

    @Override
    public Father clone() throws CloneNotSupportedException {
        Father father = null;
        father = (Father) super.clone();
        return father;
    }


    public FatherSer(String name, int age){
        this.name = name;
        this.age = age;
    }

    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 static void main(String[] args) throws IOException, ClassNotFoundException {
        FatherSer father = new FatherSer("小明父亲",32);
        PersonSer person = new PersonSer("小明",12,father);

        PersonSer personCloneable = person.serializableClone();
        personCloneable.setAge(1234);
        personCloneable.setName("小刚");
        personCloneable.getFather().setName("小刚父亲");
        System.out.println(person.getName()+":"+person.getAge());
        System.out.println(personCloneable.getName()+":" + personCloneable.getAge());
        System.out.println(person.getFather().getName()+":"+personCloneable.getFather().getName());
        System.out.println(person.getFather() == personCloneable.getFather());

    }

输出结果:

小明:12
小刚:1234
小明父亲:小刚父亲
false

通过把对象写进ByteArrayOutputStream里,再把它读取出来。

注意这个过程中所有涉及的对象都必须实现Serializable接口,由于涉及IO操作,这种方式的效率会比前面的低。

posted @ 2019-12-30 17:35  那些年的代码  阅读(735)  评论(0编辑  收藏  举报