• 模式_深拷贝


    模式_深拷贝

    举例
        刚给我的客厅做了装修,朋友也希望给他的客厅做装修,他可能会把我家的装修方案拿过来改改就成,我的装修方案就是原型。
     
    定义
        使用原型实例指定将要创建的对象类型,通过复制这个实例创建新的对象。
     
    应用场景
        当创建一些非常耗时的大对象或者创建过程非常复杂时。
     
     
    复制原型对象不一定是指从内存中进行复制,原型数据也可能保存在数据库里。
    一般情况下,OOP 语言都提供了内存中对象的复制能力,Java 语言提供了对象的浅拷贝。
     
     
    浅拷贝(Shallow copy):复制一个对象时,如果它的一个属性是引用,则复制这个引用,使之指向内存中同一个对象;
    深拷贝(Deep copy):复制一个对象时,为此属性创建了一个新对象,让其引用指向它。
     
    邮递快递的场景:
    顾客:“给我几个快递。”
    快递员:“寄往什么地方?寄给...?”
    顾客:“和上次差不多一样,只是邮寄给另外一个地址,这里是邮寄地址...“把邮寄地址的纸条给快递员。
    快递员:“好。”
        以为保存了用户以前的邮寄信息,只要复制这些数据,然后通过简单的修改就可以快速地创建新的快递数据了。
     
    注意:我们在复制新的数据时,需要特别注意不能把所有数据都复制过来,如,当对象包含主键时,不能使用原型数据的主键,必须创建一个新的主键。
     
     
    Java 的 java.lang.Object 方法里就提供了克隆方法 clone( ),原则上似乎所有类都拥有此功能,但是它的使用有如下限制:
    1. 要实现克隆,必须实现 java.lang.Cloneable 接口,否则在运行时调用 clone( ) 方法,会抛出 CloneNotSupportedException异常。
    2. 返回的是 Object类型的对象,所以使用时可能需要强制类型转换。
    3. 该方法是 protected的,如果想让外部对象使用它,必须在子类重写该方法,设定其访问范围是 public的,参见 PackageInfo 的 clone( ) 方法。
    4. Object 的 clone( ) 方法的复制是采用逐字节的方式从内存赋值数据,复制了属性了引用,而属性所指向的对象本身没有被复制,因此所复制的引用指向了相同的对象。即 浅拷贝。
     
    复制代码
    public   class  PackageInfo  implements  Cloneable {
         public   PackageInfo  clone() {
             try  {
                 return  (PackageInfo)  super .clone();
            }  catch  (CloneNotSupportedException e) {
                System. out .println( "Cloning not allowed." );
                 return   null ;
            }
        }
    
         // 静态工厂方法:根据原型创建一份副本
         public   static  PackageInfo clonePackage(String userName) {
             // 根据 userName加载一条用户以前的数据作为原型数据(数据库或其它保存的数据)
            PackageInfo prototype =  loadPackageInfo (userName);
             // 再在内存中克隆这条数据
            prototype =  prototypr .clone();
             // 初始化数据 id(主键)
            prototype. setId ( null );
             // 返回数据
             return  prototype;
        }
    }
    复制代码
     
        在实际应用中,使用原型模式创建对象图(Object Graph)非常便捷。
    对象图不是一个单个对象,而是一组聚合的对象,改组对象有一个根对象。
     
     
     
     
    深拷贝(Deep copy)的两种实现方式:
    1. 复制对象时,递归地调用属性对象的克隆方法。根据具体的类,撰写出实现特定类型的深拷贝方法。
          一般我们很难实现一个一般性的方法来完成任何类型对象的深拷贝。根据反射得到属性的类型,然后依照它的类型构造对象,但前提是:这些属性的类型必须含有一个公有的默认构造方法,否则作为一个一般性的方法,很难确定传递给非默认构造方法的参数值;此外,如果属性类型是接口或者抽象类型,必须提供查找到相关的具体类方法,作为一个一般性的方法,这个也很难办到。
       
    2. 如果类实现了 java.io.Serializable 接口,把原型对象序列化,然后反序列化后得到得对象,其实就是一个新的深拷贝对象。
     
    复制代码
    //DeepCopyBean实现了 java.io.Serializable接口
    public   class   DeepCopyBean   implements  Serializable {
         // 原始类型属性
         private   int   primitiveField ;
         // 对象属性
         private  String  objectField ;
         // 首先序列化自己到流中,然后从流中反序列化,得到得对象便是一个新的深拷贝
         public  DeepCopyBean deepCopy() {
             try  {
                ByteArrayOutputStream buf =  new  ByteArrayOutputStream();
                ObjectOutputStream o =  new  ObjectOutputStream(buf);
                o.writeObject( this );
                ObjectInputStream in =  new  ObjectInputStream(
                         new  ByteArrayInputStream(buf.toByteArray()));
                 return  (DeepCopyBean) in.readObject();
            }  catch  (Exception e) {
                e.printStackTrace();
            }
             return   null ;
        }
        // 属性 get、set 方法略...
     
     
        // 测试demo
         public   static   void  main(String[] args) {
            DeepCopyBean originalBean =  new  DeepCopyBean();
             // 创建两个 String对象,其中一个在 JVM的字符串池(String pool)里,属性引用指向另外一个在堆里的对象
            originalBean.setObjectField( new  String( "guilin" ));
            originalBean.setPrimitiveField(50);
             // 深拷贝
            DeepCopyBean newBean = originalBean.deepCopy();
             // 原始类型属性值比较:true
            System. out .println( "primitiveField ==:"
                    + (originalBean.getPrimitiveField() == newBean
                            .getPrimitiveField()));
             // 对象属性值比较:false(证明未指向相同的地址)
            System. out .println( "objectField ==:"
                    + (originalBean.getObjectField() == newBean.getObjectField()));
             // 对象属性 equals 比较:true
            System. out .println( "objectField equal:"
                    + (originalBean.getObjectField().equals(newBean
                            .getObjectField())));
        }
    }
    复制代码
    使用这种方式进行深拷贝注意:
    1. 它只能复制实现 Serializable接口类型的对象,其属性也是可序列化的;
    2. 序列化和反序列化比较耗时。
     
     
    总结:使用原型模式有以下优点:
    • 创建大的聚合对象图时,没有必要为每个层次的子对象创建相应层次的工厂类。
    • 方便实例化,只要复制对象,然后初始化对象,就可以得到想要的对象。
     
     
     
     
    标签: 设计模式java
  • 相关阅读:
    补题报告 个人赛2020.4.12
    UCF Local Programming Contest 2017 2020.4.7
    比赛:ICPC Asia Taipei-Hsinchu Regional 2019 2020.4.1
    UCF Local Programming Contest 2016 2020.3.28
    Benelux Algorithm Programming Contest 2019 2020/3/21
    比赛名:Preliminaries for Benelux Algorithm Programming Contest 2019 时间2020.3.14
    【Scala】Scala使用scalikejdbc工具连接MySQL(推荐)
    【Scala】Scala使用JDBC连接Mysql/权限问题
    【异常】Specified key was too long;max key length is 767 bytes、解决由于HDFS格式化造成Hive数据全部丢失的问题
    【异常】Hive作业异常kill :Hadoop MapReduce Error
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3186827.html
Copyright © 2020-2023  润新知