Java设计模式(6):原型模式

6,原型模式(Prototype)

6.1,基本介绍

  • 原型模式是通过原型实例指定创建对象的种类,并通过拷贝这些原型,创建新的对象
  • 原型模式是一种创建型设计模式,允许通过一个对象再创建一个可定制的对象,且不用对外暴露创建过程
  • 原型模式拷贝对象的方式,分为浅拷贝和深拷贝两种:
    • 浅拷贝通过JDK提供的API可直接进行处理,只会改变外部对象的地址,对内部引用对象地址不会改变
    • 深拷贝需要通过一些其他途径,如序列化,递归拷贝等,关联对内部引用对象地址进行改变,新对象与原对象不会再有任何联系
  • 在Spring中的使用:Bean创建方式,单例/原型

6.2,类图

在这里插入图片描述

  • 原型模式分为浅拷贝和深拷贝两种方式,两种分别对应不同的类结构

  • Prototype表示需要进行拷贝的类对象

  • 无论什么类,都会有顶层父类Object,而浅拷贝会依托于父类的clone()方法

  • 浅拷贝需要使用父类的clone()方法,则其类必须实现接口Cloneable,否则会报异常CloneNotSupportedException,具体可以看clone()的方法描述

    CloneNotSupportedException:if the object's class does not support the {@code Cloneable} interface
    
  • 深拷贝通常基于对象的序列化完成,对象序列化,其类必须实现序列化接口Serializable

6.2,浅拷贝 — 克隆方式

6.2.1,基本介绍

  • 浅拷贝是基于顶层父类Object的方法clone()进行实现,对应实体类必须实现Cloneable接口
  • 浅拷贝完成后,拷贝后对象与原对象相比,外层对象即该对象会重新构造地址创建,与原对象地址不同
  • 对于原对象内部属性,如果该属性是基本类型属性,浅拷贝会直接进行值传递,后续数据修改不会引起原对象关联修改
  • 对于内部属性是引用类型的属性,浅拷贝会直接赋值其地址到拷贝后的对象,此时原对象和拷贝后对象内部引用类型的成员属性地址公用一个,此时无论对哪一个进行修改,都会关联影响另外一个属性

6.2.1,代码示例

  • 原型类

    package com.self.designmode.prototype;
    
    import lombok.Getter;
    import lombok.Setter;
    
    /**
     * 浅拷贝
     * * 方法必须实现Cloneable接口, 不然在调用clone()方法时会报异常
     * CloneNotSupportedException  if the object's class does not support the {@code Cloneable} interface
     *
     * * 浅拷贝问题:
     * * 浅拷贝只会对直接对象进行初始化, 如果该对象内部还存在其他引用类型对象, 则不会进行初始化
     * * 此时会将内部引用类型的地址, 直接赋值给新创建的外部对象
     * * 因为地址没有变化, 如果此时对原对象的该内部引用进行修改, 会关联修改现有对象
     * @author PJ_ZHANG
     * @create 2020-07-24 17:12
     **/
    @Getter
    @Setter
    public class ShallowCopy implements Cloneable {
        private String name;
        private String addr;
        private ShallowCopy inner;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
  • 客户端

    package com.self.designmode.prototype;
    
    /**
     * 原型模式,
     * * 分为深拷贝和浅拷贝两种
     * @author PJ_ZHANG
     * @create 2020-07-24 17:11
     **/
    public class Prototype {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            ShallowCopy shallowCopy = new ShallowCopy();
            ShallowCopy inner = new ShallowCopy();
            inner.setName("inner张三");
            shallowCopy.setName("张三");
            shallowCopy.setInner(inner);
            ShallowCopy copy = (ShallowCopy) shallowCopy.clone();
            System.out.println("原对象    : " + shallowCopy.getName() + ",      地址: " + shallowCopy);
            System.out.println("拷贝对象  : " + copy.getName() + ",      地址: " + copy);
            ShallowCopy copyIn = copy.getInner();
            System.out.println("原内对象  : " + inner.getName() + ", 地址: " + inner);
            System.out.println("拷贝内对象: " + copyIn.getName() + ", 地址: " + copyIn);
            // 修改名称
            copyIn.setName("Update");
            System.out.println(inner.getName());
            System.out.println(copyIn.getName());
        }
    }
    
  • 打印结果
    在这里插入图片描述

6.3,深拷贝 — 序列化方式

6.3.1,基本介绍

  • 浅拷贝存在内部引用类型属性没有重新构建地址,深拷贝就是解决这个问题
  • 深拷贝对基本类型属性进行值复制
  • 对于成员类型属性也会重新构造内存地址,并复制该引用类型对象中每个属性的值到新构造的对象中,直到处理完该对象内部的所有可达对象(递归处理)
  • 深拷贝方式通常使用序列化来进行实现,也可通过Json进行转换,此处通过序列化实现,更清晰

6.3.2,代码示例

  • 原型类

    package com.self.designmode.prototype;
    
    import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
    import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
    import lombok.Getter;
    import lombok.Setter;
    
    import java.io.*;
    import java.util.stream.Stream;
    
    /**
     * 浅拷贝
     * * 方法必须实现Cloneable接口, 不然在调用clone()方法时会报异常
     * CloneNotSupportedException  if the object's class does not support the {@code Cloneable} interface
     *
     * * 浅拷贝问题:
     * * 浅拷贝只会对直接对象进行初始化, 如果该对象内部还存在其他引用类型对象, 则不会进行初始化
     * * 此时会将内部引用类型的地址, 直接赋值给新创建的外部对象
     * * 因为地址没有变化, 如果此时对原对象的该内部引用进行修改, 会关联修改现有对象
     * @author PJ_ZHANG
     * @create 2020-07-24 17:12
     **/
    @Getter
    @Setter
    public class ShallowCopy implements Serializable {
        private String name;
        private String addr;
        private ShallowCopy inner;
    
        public ShallowCopy deepCopy() {
            ByteArrayOutputStream byteArrayOutputStream = null;
            ObjectOutputStream objectOutputStream = null;
            ByteArrayInputStream byteArrayInputStream = null;
            ObjectInputStream objectInputStream = null;
            try {
                // 写对象到内存中
                byteArrayOutputStream = new ByteArrayOutputStream();
                objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                objectOutputStream.writeObject(this);
                objectOutputStream.flush();
                // 从内存中读对象
                byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                objectInputStream = new ObjectInputStream(byteArrayInputStream);
                Object copyObject = objectInputStream.readObject();
                return (ShallowCopy) copyObject;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    byteArrayOutputStream.close();
                    objectOutputStream.close();
                    byteArrayInputStream.close();
                    objectOutputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
    
  • 客户端

    package com.self.designmode.prototype;
    
    /**
     * 原型模式,
     * * 分为深拷贝和浅拷贝两种
     * @author PJ_ZHANG
     * @create 2020-07-24 17:11
     **/
    public class Prototype {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            ShallowCopy shallowCopy = new ShallowCopy();
            ShallowCopy inner = new ShallowCopy();
            inner.setName("inner张三");
            shallowCopy.setName("张三");
            shallowCopy.setInner(inner);
            // 浅拷贝
            // ShallowCopy copy = (ShallowCopy) shallowCopy.clone();
            // 深拷贝
            ShallowCopy copy = shallowCopy.deepCopy();
            System.out.println("原对象    : " + shallowCopy.getName() + ",      地址: " + shallowCopy);
            System.out.println("拷贝对象  : " + copy.getName() + ",      地址: " + copy);
            ShallowCopy copyIn = copy.getInner();
            System.out.println("原内对象  : " + inner.getName() + ", 地址: " + inner);
            System.out.println("拷贝内对象: " + copyIn.getName() + ", 地址: " + copyIn);
            // 修改名称
            copyIn.setName("Update");
            System.out.println(inner.getName());
            System.out.println(copyIn.getName());
        }
    }
    
  • 打印结果
    在这里插入图片描述

6.4,注意事项和细节

  • 创建的新对象比较复杂时,可以通过原型模式简化创建过程,也能够提高效率
  • 不用重新初始化对象,而是动态的获得对象运行时的状态
  • 如果原始对象发生变化(属性增减),其克隆对象也会对应的对该部分变更属性进行处理,无需修改代码
  • 在实现深拷贝的时候可能会需要比较复杂的代码(如递归处理,序列化处理)
  • 缺点:需要为每一个类配备一个克隆方法,对新增类影响不大,如果项目中突然引入,需要对全项目进行修改,势必修改大量源代码
  • 原型模式可完全通过JSON转换来实现,先序列化为字符串,再由字符串转为对象,即可实现一次深拷贝,对实体类零入侵,前面一大堆就是演示过程!!!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值