zoukankan      html  css  js  c++  java
  • 原型模式(Prototype)

    ​一、原型模式介绍

    原型模式:原型模式就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

    所谓原型模式,就是java中的克隆技术,以某个对象为原型。复制出新的对象。显然新的对象具备原型对象的特点。效率高(避免了重新执行构造过程步骤)

    克隆类似于new,但和new不同。new创建新的对象属性采用的是默认值。克隆出来的对象的属性值完全和原型对象相同。并且克隆出的新对象不会影响原型对象,克隆后。还可以再修改克隆对象的值。

    要实现原型模式,必须实现Cloneable接口,而这个接口里面是空的。

    Cloneable接口是一个空接口,使用Cloneable接口都不用导入包。而clone方法是属于Object对象的。如果要克隆某个对象的话必须实现Cloneable接口

    1
    2
    3
    4
    5
    6
    7
    @author  unascribed
     @see     java.lang.CloneNotSupportedException
     @see     java.lang.Object#clone()
     @since   JDK1.0
     */
    public interface Cloneable {
    }

    重写Object对象的clone方法,clone方法为本地方法。效率比较高

    1
    protected native Object clone() throws CloneNotSupportedException;

    如果我们要克隆某个对象有浅克隆和深克隆

    浅克隆:copy该对象,然后保留该对象原有的引用。也就是说不克隆该对象的属性。

    深克隆:copy该对象,并且把该对象的所有属性也克隆出一份新的。

    二、代码实现

    1、浅克隆代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    /**
     * 原型模式:浅克隆
     * Cloneable是一个空接口(标记接口),是一个规范。但是如果要克隆这个类对象的话必须实现Cloneable接口
     */
    public class Sheep implements Cloneable{
        private String sname;
        private Date birthday;
         
        /**
         * 重写Object对象的clone方法
         */
        @Override
        protected Object clone() throws CloneNotSupportedException {
            //直接调用Object对象的clone方法
            Object obj = super.clone();
            return obj;
        }
        //省略get,set方法和构造方法
         
    }
     
    /**
     * 测试原型模式(浅克隆)
     */
    public class Test {
        public static void main(String[] args) throws Exception {
            Date date = new Date(1274397294739L);
            Sheep s1 = new Sheep("原型羊",date);
            Sheep s2 = (Sheep) s1.clone();//克隆一个羊
            System.out.println(s1);
            System.out.println(s1.getSname());
            System.out.println("原日期:"+s1.getBirthday());
            date.setTime(34732834827389L);//改变原有date的值
            System.out.println("改变后的日期:"+date.toString());
             
            //克隆羊的信息
            System.out.println("---------------------------------");
            System.out.println(s2);
            System.out.println(s2.getSname());
            System.out.println(s2.getBirthday());//此时的birthday日期使用的是改变后的日期对象引用
        }
    }

    最后的结果为:克隆的对象仍然保留了原有对象的引用,值随着改变而改变

    1
    2
    3
    4
    5
    6
    7
    8
    com.fz.prototype.Sheep@153f67e
    原型羊
    原日期:Fri May 21 07:14:54 CST 2010
    改变后的日期:Mon Aug 22 17:40:27 CST 3070
    ---------------------------------
    com.fz.prototype.Sheep@18f51f
    原型羊
    Mon Aug 22 17:40:27 CST 3070

    2、深克隆代码实现:克隆对象的同时,把该对象的属性也连带着克隆出新的。

    深克隆只需要在clone方法中将该对象的属性也克隆即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
     * 重写Object对象的clone方法
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //直接调用Object对象的clone方法
        Object obj = super.clone();
        //深克隆:把对象下的所有属性也克隆出来
        Sheep22 s = (Sheep22) obj;
        s.birthday = (Date) this.birthday.clone();
        return s;
    }

    测试代码不变,结果则会变了。克隆了之后把原来的日期改变后,克隆的对象2的属性则不会被影响。

    1
    2
    3
    4
    5
    6
    7
    8
    com.fz.prototype.Sheep2@15bdc50
    原型羊
    原日期:Fri May 21 07:14:54 CST 2010
    改变后的日期:Mon Aug 22 17:40:27 CST 3070
    ---------------------------------
    com.fz.prototype.Sheep2@18f51f
    原型羊
    Fri May 21 07:14:54 CST 2010

    3、通过序列化和反序列化来实现深克隆对象:序列化需要原型对象实现Serializable接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.util.Date;
     
    /**
     * 测试原型模式(利用序列化和反序列化实现深克隆)
     */
    public class Test3 {
        public static void main(String[] args) throws Exception {
            Date date = new Date(1274397294739L);
            Sheep s1 = new Sheep("原型羊",date);
    //      Sheep s2 = (Sheep) s1.clone();//克隆一个羊
             
            //使用序列化和反序列化实现深复制
            //1、将s1对象序列化为一个数组
            //通过ObjectOutputStream流将s1对象读出来给ByteArrayOutputStream流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream    oos = new ObjectOutputStream(bos);
            oos.writeObject(s1);
            //ByteArrayOutputStream流将对象信息转成byte数组,这样byte数组里就包含了对象的数据
            byte[] bytes = bos.toByteArray();
             
            //2、将字节数组中的内容反序列化为一个Sheep对象
            //通过ByteArrayInputStream流读入bytes字节数组中数据,然后传给ObjectInputStream对象输入流
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream    ois = new ObjectInputStream(bis);
            //通过ObjectInputStream返回一个Sheep对象
            Sheep s2 = (Sheep) ois.readObject();
     
            //原型羊的信息
            System.out.println(s1);
            System.out.println("原日期:"+s1.getBirthday());
            date.setTime(34732834827389L);//改变原有date的值
            System.out.println("改变后的日期:"+date.toString());
            //克隆羊的信息
            System.out.println("---------------------------------");
            System.out.println(s2);
            System.out.println(s2.getBirthday());
        }
    }

    通过序列化和反序列化的结果,最终结果还是和深克隆一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    com.fz.prototype.Sheep@1a116c9
     
    原日期:Fri May 21 07:14:54 CST 2010
     
    改变后的日期:Mon Aug 22 17:40:27 CST 3070
     
    ---------------------------------
     
    com.fz.prototype.Sheep@7eb6e2
     
    Fri May 21 07:14:54 CST 2010

    三、测试克隆对象的效率

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    package com.fz.prototype;
    /**
     * 测试clone对象的效率
     */
    public class TestClone {
        //new 对象
        public static void testNew(int size){
            long start = System.currentTimeMillis();
            for (int i = 0; i < size; i++) {
                Laptop l = new Laptop();
            }
            long end = System.currentTimeMillis();
            System.out.println("new 对象耗时:"+(end-start));
        }
        //clone 对象
        public static void testClone(int size){
            long start = System.currentTimeMillis();
            Laptop l = new Laptop();
            for (int i = 0; i < size; i++) {
                try {
                    Laptop temp = (Laptop) l.clone();
                catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println("clone 对象耗时:"+(end-start));
        }
        public static void main(String[] args) {
            testNew(1000);
            testClone(1000);
        }
    }
     
    class Laptop implements Cloneable{
        public Laptop() {
            //模拟创建Laptop对象的时候比较耗时
            try {
                Thread.sleep(10);
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

    最后结果为:

    new 对象耗时:10063

    clone 对象耗时:10

    四、使用场景

    原型模式适用场景:如果某个对象new的过程中很耗时,则可以考虑使用原型模式。

    Spring框架中bean对象的创建就两种模式:单例模式或者原型模式



    Java23种设计模式学习笔记【目录总贴】

    参考资料:

      大话设计模式(带目录完整版).pdf

      HEAD_FIRST设计模式(中文版).pdf

      尚学堂_高淇_java300集最全视频教程_【GOF23设计模式】

  • 相关阅读:
    如何用最小的代价完成爬虫需求
    那些年我们一起守护的“密”
    自动化中间人攻击工具subterfuge小实验
    XP操作系统设置:[82]关机快捷键
    如何绕过Win8、Win10的systemsetting与注册表校验设置默认浏览器
    定时启动和关闭指定程序的方法
    Delphi 的内存操作函数(2): 给数组指针分配内存
    Delphi 的内存操作函数(1): 给字符指针分配内存
    Java Gearman Service
    分布式计算框架Gearman原理详解
  • 原文地址:https://www.cnblogs.com/cxxjohnson/p/6403949.html
Copyright © 2011-2022 走看看