原型模式详解附有代码案例分析(浅克隆和深克隆的相关解析)

news/2024/5/19 3:41:10 标签: java, 设计模式, 编程语言, 反射, 接口

原型模式详解

    • 一、原型模式的概念与角色
      • (一)、原型模式的概念
      • (二)、原型模式的角色
    • 二、原型模式的应用场景
    • 三、原型模式的通用写法
    • 四、原型模式——使用Java语言提供的clone()方法
      • (一)、Java语言提供的clone()方法
      • (二)、通用写法的改进——代码示例
    • 五、浅克隆与深克隆
      • (一)、浅克隆与深克隆的特点
        • 1、浅克隆的特点
        • 2、深克隆的特点
      • (二)、浅克隆的代码示例
      • (三)、深克隆的代码示例
    • 六、深克隆对单例模式的破坏
      • (一)、饿汉式单例模式
      • (二)、测试代码
      • (三)、结果显示
      • (四)、解决方法
    • 七、原型模式的优缺点
      • (一)、优点
      • (二)、缺点
    • 八、设计模式的相关博客文章链接
      • 1、七大设计原则的简单解释(包含合成复用原则),简单理解、快速入门,具备案例代码
      • 2、工厂模式详解附有代码案例分析(简单工厂,工厂方法,抽象工厂)
      • 3、单例模式详解及代码案例与应用场景(饿汉式单例模式、懒汉式单例模式、注册式单例模式)
      • 4、原型模式详解附有代码案例分析(浅克隆和深克隆的相关解析)
      • 5、建造者模式详解附有代码案例分析(包含建造者模式与工厂模式的区别分析)
      • 6、门面模式详解附有代码案例分析
      • 7、装饰者模式详解附有代码案例分析
      • 8、享元模式详解附有代码案例分析(包含享元模式的源码应用分析——String中的享元模式应用、Integer中的享元模式应用)
      • 9、组合模式详解附有代码案例分析(包含透明组合模式、安全组合模式的代码示例)
      • 10、桥接模式详解附有代码案例分析
      • 11、适配器模式详解附有代码案例分析(包含类适配器、对象适配器以及接口适配器的代码示例)
      • 12、委派模式详解附有代码案例分析(包含委派模式在JDK中的源码示例解析)
      • 13、模板方法模式详解附有代码案例分析(包含模板方法模式重构JDBC操作业务代码示例)
      • 14、策略模式详解附有代码案例分析(包含策略模式在源码中的应用以及代码示例)
      • 15、责任链模式详解附有代码案例分析(包含责任链模式与建造者模式的结合代码案例)
      • 16、迭代器模式详解附有代码案例分析(包含迭代器模式的源码应用分析)
      • 17、命令模式详解附有代码案例分析(包含命令模式的源码应用分析)
      • 18、状态模式详解附有代码案例分析(包含状态模式与其他相关设计模式的对比)
      • 19、备忘录模式详解附有代码案例分析
      • 20、中介者模式详解附有代码案例分析
      • 21、解释器模式详解附有代码案例分析
      • 22、观察者模式详解附有代码案例分析(包含观察者模式使用JDK方式实现)
      • 23、访问者模式详解附有代码案例分析

一、原型模式的概念与角色

(一)、原型模式的概念

​ 原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,属于创建型模式。

​ 原型模式的核心在于拷贝原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需经历耗时的对象初始化过程(不调用构造函数),性能提升了许多。当对象的构建过程比较耗时时,可以利用当前系统中已存在的对象作为原型,对其进行克隆(一般是基于二进制流的复制),躲避初始化过程,使得新对象的创建时间大大减少。

对不通过new关键字,而是通过对象拷贝来实现创建对象的模式就称作原型模式。

(二)、原型模式的角色

  • 客户(Client):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。

  • 抽象原型(Prototype):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。

  • 具体原型(Concrete Prototype):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

二、原型模式的应用场景

1、类初始化消耗资源较多

2、new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)

3、构造函数比较复杂

4、循环体中生产大量对象时

三、原型模式的通用写法

抽象原型接口Prototype

java">public interface Prototype<T> {
    T clone();
}

具体原型:ConcretePrototype

java">public class ConcretrPrototype implements Prototype {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public ConcretrPrototype clone() {
        ConcretrPrototype concretrPrototype = new ConcretrPrototype();
        concretrPrototype.setAge(this.age);
        concretrPrototype.setName(this.name);
        return concretrPrototype;
    }

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

具体调用:Main

java">public class Main {
    public static void main(String[] args) {
        ConcretrPrototype prototype = new ConcretrPrototype();
        prototype.setAge(18);
        prototype.setName("Tom");
        System.out.println(prototype);
        ConcretrPrototype cloneType = prototype.clone();
        System.out.println(cloneType);
    }
}

四、原型模式——使用Java语言提供的clone()方法

(一)、Java语言提供的clone()方法

​ 众所周知,所有的Java类都继承自 java.lang.Object。事实上,Object 类提供一个 clone() 方法,可以将一个Java对象复制一份。因此在Java中可以直接使用 Object 提供的 clone() 方法来实现对象的克隆,Java语言中的原型模式实现很简单。

​ 需要注意的是能够实现克隆的Java类必须实现一个 标识接口 Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个 CloneNotSupportedException 异常。

(二)、通用写法的改进——代码示例

java">public class ConcretrPrototype implements Cloneable {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public ConcretrPrototype clone() {
        try {
            return (ConcretrPrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }

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

五、浅克隆与深克隆

(一)、浅克隆与深克隆的特点

1、浅克隆的特点

(1)、在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

(2)、简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制

(3)、在Java语言中,通过覆盖Object类clone()方法可以实现浅克隆。

2、深克隆的特点

(1)、在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

(2)、简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

(3)、在Java语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

(二)、浅克隆的代码示例

具体原型:ConcretePrototype

增加属性:List hobbies

java">public class ConcretrPrototype implements Cloneable {
    private int age;
    private String name;
    private List<String> hobbies;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public ConcretrPrototype clone() {
        try {
            return (ConcretrPrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "ConcretrPrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}

具体调用:Main

java">public class Main {
    public static void main(String[] args) {
        ConcretrPrototype prototype = new ConcretrPrototype();
        prototype.setAge(18);
        prototype.setName("Tom");

        List<String> hobbies = new ArrayList<>();
        hobbies.add("游戏");
        hobbies.add("小说");
        prototype.setHobbies(hobbies);

        // 拷贝原型对象
        ConcretrPrototype cloneType = prototype.clone();
        cloneType.getHobbies().add("睡觉");

        System.out.println("原型对象:" + prototype);
        System.out.println("拷贝对象:" + cloneType);
    }
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5xc3ADtP-1600863898092)(C:\Users\huang\AppData\Roaming\Typora\typora-user-images\image-20200923173010505.png)]

(三)、深克隆的代码示例

具体原型:ConcretePrototype

改写clone()方法

java">public class ConcretrPrototype implements Cloneable, Serializable {
    private int age;
    private String name;
    private List<String> hobbies;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public ConcretrPrototype 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);

            return (ConcretrPrototype) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "ConcretrPrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}

具体调用:Main

java">public class Main {
    public static void main(String[] args) {
        ConcretrPrototype prototype = new ConcretrPrototype();
        prototype.setAge(18);
        prototype.setName("Tom");

        List<String> hobbies = new ArrayList<>();
        hobbies.add("游戏");
        hobbies.add("小说");
        prototype.setHobbies(hobbies);

        // 拷贝原型对象
        ConcretrPrototype cloneType = prototype.deepClone();
        cloneType.getHobbies().add("睡觉");

        System.out.println("原型对象:" + prototype);
        System.out.println("拷贝对象:" + cloneType);
        System.out.println(prototype == cloneType);

        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("拷贝对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());
    }
}

运行结果:

在这里插入图片描述

六、深克隆对单例模式的破坏

(一)、饿汉式单例模式

java">public class HungrySingleton implements Serializable, Cloneable {
    private final static HungrySingleton hungrySingleton;

    static {
        hungrySingleton = new HungrySingleton();
    }

    private HungrySingleton() {
        if (hungrySingleton != null) {
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }

    private Object readResolve() {
        return hungrySingleton;
    }

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

(二)、测试代码

java">public class Test {
    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        HungrySingleton hungrySingleton = HungrySingleton.getInstance();
        Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
        method.setAccessible(true);
        HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
        System.out.println(hungrySingleton);
        System.out.println(cloneHungrySingleton);
    }
}

(三)、结果显示

​ 由图可知,该单例模式被破坏,生成的是两个不同的对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uIxC6wpc-1600863898116)(C:\Users\huang\AppData\Roaming\Typora\typora-user-images\image-20200923201827217.png)]

(四)、解决方法

​ 如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止克隆破坏单例只需要禁止深克隆便可。要么单例类不实现Cloneable接口;要么重写clone()方法,在clone()方法中返回单例对象即可。

java">    @Override
    protected Object clone() throws CloneNotSupportedException {
        return hungrySingleton;
    }

七、原型模式的优缺点

(一)、优点

1、性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多

2、可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可以辅助实现撤销操作。

(二)、缺点

1、需要为每一个类配置一个克隆方法

2、克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。

3、在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深拷贝、浅拷贝需要运用得当。

八、设计模式的相关博客文章链接

1、七大设计原则的简单解释(包含合成复用原则),简单理解、快速入门,具备案例代码

链接: 七大设计原则的简单解释(包含合成复用原则),简单理解、快速入门,具备案例代码.

2、工厂模式详解附有代码案例分析(简单工厂,工厂方法,抽象工厂)

链接: 工厂模式详解附有代码案例分析(简单工厂,工厂方法,抽象工厂).

3、单例模式详解及代码案例与应用场景(饿汉式单例模式、懒汉式单例模式、注册式单例模式)

链接: 单例模式详解及代码案例与应用场景(饿汉式单例模式、懒汉式单例模式、注册式单例模式).

4、原型模式详解附有代码案例分析(浅克隆和深克隆的相关解析)

链接: 原型模式详解附有代码案例分析(浅克隆和深克隆的相关解析).

5、建造者模式详解附有代码案例分析(包含建造者模式与工厂模式的区别分析)

链接: 建造者模式详解附有代码案例分析(包含建造者模式与工厂模式的区别分析).

6、门面模式详解附有代码案例分析

链接: 门面模式详解附有代码案例分析.

7、装饰者模式详解附有代码案例分析

链接: 装饰者模式详解附有代码案例分析.

8、享元模式详解附有代码案例分析(包含享元模式的源码应用分析——String中的享元模式应用、Integer中的享元模式应用)

链接: 享元模式详解附有代码案例分析(包含享元模式的源码应用分析——String中的享元模式应用、Integer中的享元模式应用).

9、组合模式详解附有代码案例分析(包含透明组合模式、安全组合模式的代码示例)

链接: 组合模式详解附有代码案例分析(包含透明组合模式、安全组合模式的代码示例).

10、桥接模式详解附有代码案例分析

链接: 桥接模式详解附有代码案例分析.

11、适配器模式详解附有代码案例分析(包含类适配器、对象适配器以及接口适配器的代码示例)

链接: 适配器模式详解附有代码案例分析(包含类适配器、对象适配器以及接口适配器的代码示例).

12、委派模式详解附有代码案例分析(包含委派模式在JDK中的源码示例解析)

链接: 委派模式详解附有代码案例分析(包含委派模式在JDK中的源码示例解析).

13、模板方法模式详解附有代码案例分析(包含模板方法模式重构JDBC操作业务代码示例)

链接: 模板方法模式详解附有代码案例分析(包含模板方法模式重构JDBC操作业务代码示例).

14、策略模式详解附有代码案例分析(包含策略模式在源码中的应用以及代码示例)

链接: 策略模式详解附有代码案例分析(包含策略模式在源码中的应用以及代码示例).

15、责任链模式详解附有代码案例分析(包含责任链模式与建造者模式的结合代码案例)

链接: 责任链模式详解附有代码案例分析(包含责任链模式与建造者模式的结合代码案例).

16、迭代器模式详解附有代码案例分析(包含迭代器模式的源码应用分析)

链接: 迭代器模式详解附有代码案例分析(包含迭代器模式的源码应用分析).

17、命令模式详解附有代码案例分析(包含命令模式的源码应用分析)

链接: 命令模式详解附有代码案例分析(包含命令模式的源码应用分析).

18、状态模式详解附有代码案例分析(包含状态模式与其他相关设计模式的对比)

链接: 状态模式详解附有代码案例分析(包含状态模式与其他相关设计模式的对比).

19、备忘录模式详解附有代码案例分析

链接: 备忘录模式详解附有代码案例分析.

20、中介者模式详解附有代码案例分析

链接: 中介者模式详解附有代码案例分析.

21、解释器模式详解附有代码案例分析

链接: 解释器模式详解附有代码案例分析.

22、观察者模式详解附有代码案例分析(包含观察者模式使用JDK方式实现)

链接: 观察者模式详解附有代码案例分析(包含观察者模式使用JDK方式实现).

23、访问者模式详解附有代码案例分析

链接: 访问者模式详解附有代码案例分析.


http://www.niftyadmin.cn/n/815981.html

相关文章

centos 6.7 mysql密码忘记_CentOS 7安装MySQL5.7以及忘记root密码怎么办

一、通过Yum命令安装1.下载rpm安装源rpm文件地址&#xff1a;https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm1)通过wget命令下载文件[rootlocalhost yum.repos.d]# wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm--201…

sae mysql api_为SAE写的一个mysql操作类

最近在新浪的云平台(SAE&#xff0c;https://sae.sina.com.cn)中做应用&#xff0c;本来使用SAE提供的mysql操作类SaeMysql(https://apidoc.sinaapp.com/sae/SaeMysql.html)&#xff0c;但是有些不方便&#xff1a;1、SaeMysql没有提供完整的增删查改方法&#xff0c;插入数据、…

建造者模式详解附有代码案例分析(包含建造者模式与工厂模式的区别分析)

建造者模式详解一、建造者模式的概念与角色&#xff08;一&#xff09;、建造者模式的概念&#xff08;二&#xff09;、建造者模式的角色二、建造者模式的应用场景三、建造者的基本写法四、建造者模式的链式写法五、建造者模式的优缺点&#xff08;一&#xff09;、优点&#…

Window环境下MySQL+Nginx+PHP的搭建

Window环境下MySQLNginxPHP的搭建一&#xff1a;下载软件二&#xff1a;安装软件三、Nginx的搭建四、PHP的搭建&#xff08;一&#xff09;、指定PHP的ext路径&#xff08;二&#xff09;、支持MySQL&#xff08;三&#xff09;、支持Nginx&#xff08;四&#xff09;、其他配置…

门面模式详解附有代码案例分析

门面模式一、门面模式的概念和角色&#xff08;一&#xff09;、门面模式的概念&#xff08;二&#xff09;、门面模式的角色二、门面模式的使用场景三、门面模式的通用写法四、门面模式的优缺点&#xff08;一&#xff09;、优点&#xff1a;&#xff08;二&#xff09;、缺点…

windows服务器安装mysql命令_Windows服务器安装MySQL 5.5图文教程

虽然现在不少PHP套件直接安装好了PHP和首先进下载下载完MySQL以后&#xff0c;开始安装&#xff0c;详细的安装过程请参考下面图文介绍双击安装文件&#xff0c;弹出安装界面下一步勾选接受协议&#xff0c;下一步三种安装模式&#xff1a;Typical(典型安装)、Custom(自定义安装…

装饰者模式详解附有代码案例分析

装饰者模式一、装饰者模式的概念与角色&#xff08;一&#xff09;、装饰者模式的概念&#xff08;二&#xff09;、装饰者的角色二、装饰者模式的应用场景三、无使用装饰者模式的代码示例四、装饰者模式的代码示例五、装饰者模式的优缺点&#xff08;一&#xff09;、优点&…

c# mysql 编码_用C#往MySql数据库表中插入中文,显示的全是问号

部分代码如下&#xff1a;stringsaveStudent"insertintotestvalues("schoolID","schoolName","gradeNum","classNum","studentID","studentName","chinese","math","physi...部分代…