java设计模式系列4——原型设计模式

概述

  • 是一种对象创建型模式,使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,主要用于创建重复的对象,同时又能保证性能
  • 工作原理是将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程
  • 应该是最简单的设计模式了,实现一个接口,重写一个方法即完成了原型模式

核心组成

  • Prototype: 声明克隆方法的接口,是所有具体原型类的公共父类,Cloneable接口
  • ConcretePrototype : 具体原型类
  • Client: 让一个原型对象克隆自身从而创建一个新的对象

应用场景

  • 创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得
  • 如果系统要保存对象的状态,做备份使用
编码实现

定义一个对象,实现Cloneable接口,重写clone方法

public class Person implements Cloneable{

    private String name;

    public Person() {
        System.out.println("构造方法被调用了");
    }

    public String getName() {
        return name;
    }

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

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person)super.clone();
    }
}

使用:

public static void main(String[] args) throws CloneNotSupportedException {
    Person person = new Person();
    person.setName("张三");

    Person person1 = person.clone();
    person.setName("李四");
    
     Person person1 = person.clone();
    person.setName("王五");
}

观察控制台,只有一句话:构造方法被调用了,证明构造方法只被调用了一次。

场景说明:如果Person对象里面有几十种属性,但只需要就改变其中的几个属性,使用原型设计模式会方便很多。

问题剖析——深拷贝与浅拷贝

看起来是不是很简单?别着急还没完呢,如果里面是一个复杂的数据类型呢?
例如对象为List,通过DEBUG模式发现,拷贝后的内存地址是一样的,因此当一个对象修改了List内容,其他对象都会同步修改,如果我们不想同步修改该怎么办呢?
在这里插入图片描述

  • 浅拷贝:如果原型对象的成员变量是基本数据类型(int、double、byte、boolean、char等),将复制一份给克隆对象;
    如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址
    通过覆盖Object类的clone()方法可以实现浅克隆
  • 深拷贝:无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象,如果需要实现深克隆,可以通过序列化(Serializable)等方式来实现
  • 原型模式是内存二进制流的拷贝,比new对象性能高很多,使用的时候记得注意是选择浅拷贝还是深拷贝

我们让拷贝的对象进行一次序列化与反序列化,这样内存地址就改变了。既然涉及到了序列化就需要实现Serializable接口

public class Person implements Cloneable, Serializable {

    private String name;

    private List<String> myList;

    public Person() {
        System.out.println("构造方法被调用了");
    }

   //get&&set

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person)super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", myList=" + myList +
                '}';
    }

    /**
     * 深拷贝
     * 原理:经过了序列化与反序列化,内存地址会发送变更
     */
    public Object deepClone() {

        try {
            //输出 序列化
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            //输入 反序列化
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            Person copyObj = (Person) ois.readObject();

            return copyObj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

测试:

    public static void main(String[] args) throws CloneNotSupportedException {
        ArrayList<String> myList = new ArrayList<String>() {{
            add("张三丰");
            add("赵六");
        }};
        Person person = new Person();
        person.setName("张三");
        person.setMyList(myList);

        // 浅拷贝
        Person person1 = person.clone();
        person1.setName("李四");
        person1.getMyList().add("测试1");

        // 深拷贝
        Person person2 = (Person) person.deepClone();
        person.setName("赵六");
        person2.getMyList().add("测试2");

        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);
    }

结果

构造方法被调用了
Person{name='赵六', myList=[张三丰, 赵六, 测试1]}
Person{name='李四', myList=[张三丰, 赵六, 测试1]}
Person{name='张三', myList=[张三丰, 赵六, 测试1, 测试2]}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CV大魔王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值