对象的分身术 -- 原型模式

在开发中常常会遇到需要产生两个一模一样的对象的情况,这个时候原型模式就可以派上用场。原型模式就是通过拷贝原型对象来产生产生新对象的一种创建型模式。

通用的原型模式

可以通过直接将原型的属性值赋值给新对象,从而产生一个一模一样的对象。
public class Dog {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dog clone() {
        Dog dog = new Dog();
        dog.setName(this.name);
        return dog;
    }
}

Object.clone()

在java中,Object提供了通用的clone方法,任何类都有这个方法。
要调用这个方法需要实现Cloneable接口,这个接口不提供任何方法,它只是一个标记,用来标识该类可以克隆。
如果不实现该接口,调用clone方法的时候会抛出CloneNotSupportedException 异常。

public class Prototype implements Cloneable{

    private String name;
    private int 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;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }
}

浅拷贝和深拷贝

我们来做个测试

public class Prototype implements Cloneable {

    private String name;
    private int age;
    private Dog dog;

    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 Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }

    @Override
    public String toString() {
        return "Prototype{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", dog=" + dog +
                '}';
    }
}

public class Dog implements Cloneable {

    public Dog() {
    }

    public Dog(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new Prototype();
        prototype.setName("小王");
        prototype.setAge(10);
        Dog wangcai = new Dog("旺财");
        prototype.setDog(wangcai);

        Prototype clone = prototype.clone();
        System.out.println(prototype);
        System.out.println(clone);
        System.out.println(prototype == clone);

    }
}

打印结果如下:

Prototype{name='小王', age=10, dog=Dog{name='旺财'}}
Prototype{name='小王', age=10, dog=Dog{name='旺财'}}
false
true

可以看到,两个对象的属性一样,但是两个对象引用的dog是同一个对象,而不是产生两个属性一样的新对象。
所以说Java提供的Object.clone()方法是一种浅拷贝.

那么如何实现深拷贝呢?
比较简单的方式是将Prototype的clone方法写成下面这样。

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        Prototype clone = (Prototype) super.clone();
        clone.setDog((Dog) this.dog.clone());
        return clone;
    }

结果如下:

Prototype{name='小王', age=10, dog=Dog{name='旺财'}}
Prototype{name='小王', age=10, dog=Dog{name='旺财'}}
false
false

可以看到两只小狗虽然一模一样,但不是同一只狗。
在Java中还可以通过序列化再反序列化的方式实现深拷贝,这样的写法比较通用,任何对象都可以用同一种写法。
首先Prototype和Dog类都需要实现Serializable接口,Serializable和Cloneable一样,不需要实现方法就可以使用。
我们的深拷贝方法实现如下:

    public Prototype deepClone() throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
        ) {
            objectOutputStream.writeObject(this);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (Prototype) ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
posted @ 2020-07-17 16:56  BarneyMosby  阅读(165)  评论(0)    收藏  举报