GOF23设计模式之原型模式

本文深入探讨了Java中的原型模式,解释了克隆技术与new的区别,详细阐述了浅克隆和深克隆的概念及实现方式。通过示例展示了如何通过实现Cloneable接口和重写clone方法进行浅复制和深复制,并利用序列化与反序列化实现深克隆。此外,还对比了两种克隆方式的效率,强调在数据准备繁琐或访问权限受限的情况下使用原型模式的优势。
摘要由CSDN通过智能技术生成

原型模式

一、前置知识

场景

  1. 思考一下:克隆技术是怎么样的过程? 克隆羊多利大家还记得吗?
  2. javascript语言中的,继承怎么实现?那里面也有prototype,大家还记得吗?

原型模式

通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。就是java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点。

  • 优势有:效率高(直接克隆,避免了重新执行构造过程步骤) 。
  • 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的 对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后, 再修改克隆对象的值

原型模式实现

  • Cloneable接口和clone方法
  • Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了 clone()方法替我们做了绝大部分事情。

注意用词:克隆和拷贝一回事!

二、原型模式prototype

浅克隆存在的问题

被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

深克隆如何实现?

深克隆把引用的变量指向复制过的新对象,而不是原有的被引用的对象。

深克隆:让已实现Clonable接口的类中的属性也实现Clonable接口

基本数据类型和String能够自动实现深度克隆(值的复制)

在这里插入图片描述

三、示例

(一)实现Cloneable接口和clone方法

  1. 多利实体类Sheep
/**
 * 多利羊
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Sheep implements Cloneable {
    private String sName;
    private Date brithDay;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //直接调用的object 对象的clone方法
        Object obj = super.clone();
        return obj;
    }
}
  1. 测试类
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("少利", new Date());
        System.out.println(sheep);
        sheep.setBirthDay(new Date(1234565555L));
        try {
            Sheep sheepClone = (Sheep) sheep.clone();
            sheepClone.setSName("多利");
            System.out.println(sheepClone);
            System.out.println(sheep == sheepClone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

在这里插入图片描述

这种克隆只是把属性地址指向了原有的地址,当原有地址内容发生改变后,会同步改变。

浅复制

通过修改创建时间来进行测试浅复制

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("少利", new Date());
        System.out.println(sheep);
        sheep.setBrithDay(new Date(1234565555L));
        try {
            Sheep sheepClone = (Sheep) sheep.clone();
            System.out.println(sheepClone);
            System.out.println(sheep == sheepClone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

克隆的对象也同步发生了改变,因此得证。这种只复制原有对象地址的,叫做浅克隆。

深复制

通过把属性也进行复制来完成

  1. 修改Sheep ,添加属性克隆代码
/**
 * 多利羊2
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Sheep2 implements Cloneable {
    private String sName;
    private Date birthDay;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //直接调用的object 对象的clone方法
        Object obj = super.clone();
        //添加如下代码实现深复制
        Sheep2 sheep = (Sheep2) obj;
        //把属性也进行克隆!
        sheep.birthDay=(Date) this.birthDay.clone();
        return obj;
    }
}
  1. 测试代码
public class Client2 {
    public static void main(String[] args) {
        Date date = new Date();
        Sheep2 sheep = new Sheep2("少利",date);
        System.out.println("修改前"+sheep);
        Sheep2 clone = null;
        try {
            clone = (Sheep2) sheep.clone();
            clone.setSName("多利");
            System.out.println("克隆对象"+clone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        date.setTime(1131515151111L);
        System.out.println("修改后"+sheep);
        System.out.println("克隆对象修改后"+clone);

    }
}

运行结果k

在这里插入图片描述

克隆对象脱离与原有对象的地址连接关系

(二)利用序列化和反序列化实现*深复制*

  1. 测试类3
/**
 * 使用序列化和反序列化的方法实现深复制
 */
public class Client3 {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Date date = new Date();
        Sheep2 sheep = new Sheep2("少利", date);
        System.out.println("修改前" + sheep);
        //使用序列化和反序列化实现深复制
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);

        oos.writeObject(sheep);
        byte[] bytes = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);

        Sheep2 sheep2 = (Sheep2) ois.readObject();
        sheep2.setSName("多利");
        System.out.println("克隆对象" + sheep2);

        date.setTime(1131515151111L);
        System.out.println("修改后" + sheep);
        System.out.println("克隆对象修改后" + sheep2);

    }
}
  1. Sheep2 实现序列化接口,去除原有克隆属性代码
/**
 * 多利羊2 实现了序列化接口
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Sheep2 implements Cloneable, Serializable {
    private String sName;
    private Date birthDay;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //直接调用的object 对象的clone方法
        Object obj = super.clone();
        return obj;
    }
}

也完成了深复制,运行结果如下

在这里插入图片描述

四、效率对比

  1. 创建Laptop类
public class Laptop implements Cloneable{
    public Laptop(){
        //模拟创建很耗时的情况
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();  //直接调用object对象的clone()方法!
        return obj;
    }
}
  1. client4 进行测试
/**
 *  测试普通new方式创建对象和clone方式创建对象的效率差异!
 *  如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
 */
public class Client4 {

    public static void testNew(int size){
        long start = System.currentTimeMillis();
        for(int i=0;i<size;i++){
            Laptop t = new Laptop();
        }
        long end = System.currentTimeMillis();
        System.out.println("new的方式创建耗时:"+(end-start));
    }

    public static void testClone(int size) throws CloneNotSupportedException{
        long start = System.currentTimeMillis();
        Laptop t = new Laptop();
        for(int i=0;i<size;i++){
            Laptop temp = (Laptop) t.clone();
        }
        long end = System.currentTimeMillis();
        System.out.println("clone的方式创建耗时:"+(end-start));
    }

    public static void main(String[] args) throws Exception {
        testNew(1000);
        testClone(1000);
    }
}

运行结果

在这里插入图片描述

开发中的应用场景

原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone 的方法创建一个对象,然后由工厂方法提供给调用者。

spring中bean的创建实际就是两种:单例模式和原型模式。(当然,原型 模式需要和工厂模式搭配起来)

五、使用场景

通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值