深入浅出设计模式 - 原型模式

news2024/11/14 15:04:48

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

文章目录

  • 1、什么是原型模式
  • 2、原型模式有哪些应用场景
  • 3、浅克隆
  • 4、深克隆
  • 5、有几种实现深克隆的方式
    • 5.1、通过序列化的方式实现深克隆
    • 5.3、使用 Apache Commons进行深克隆
    • 5.3、递归克隆引用

1、什么是原型模式

原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象,而不是通过实例化类来创建。在原型模式中,一个原型对象被克隆以生成新的对象,新对象的属性可以和原型对象相同,也可以有所改变。这种方式可以避免重复创建对象,提高创建对象的效率。

在实现原型模式时,需要让原型对象实现一个克隆方法,该方法返回一个新的对象,新对象的属性与原型对象相同。在使用原型对象创建新对象时,只需要调用原型对象的克隆方法即可。

原型模式可以分为浅克隆和深克隆两种类型。浅克隆只复制对象的基本属性,而不复制对象中的引用类型属性;深克隆则会复制对象中的所有属性,包括引用类型属性。需要根据具体的业务需求选择使用哪种克隆方式。

原型模式的优点是可以避免重复创建对象,提高创建对象的效率,同时也可以减少内存占用。缺点是需要让原型对象实现克隆方法,这可能会增加代码量和复杂度。

2、原型模式有哪些应用场景

原型模式适用于以下场景:

  1. 对象的创建成本较大,例如创建对象需要进行复杂的计算、连接数据库或者网络等操作,使用原型模式可以避免这些操作,提高对象的创建效率。

  2. 需要避免重复创建对象,例如在多次创建相似对象时,使用原型模式可以避免重复创建,提高性能。

  3. 需要动态地创建对象,根据不同的条件创建不同的对象,使用原型模式可以通过复制已有的对象来创建新的对象,而不需要知道创建新对象的具体细节。

  4. 需要保护对象的复杂状态,例如创建一个对象时需要进行一系列的操作才能得到一个完整的对象,使用原型模式可以复制一个已经完成了这些操作的对象,而不需要重新进行这些操作。

  5. 需要减少子类的构造,例如在一个类的子类中需要创建相同的对象,使用原型模式可以避免在每个子类中都创建相同的对象,减少代码的重复。

总之,原型模式适用于需要创建相似对象并且创建对象的成本较高的场景,可以提高性能和代码复用性。

3、浅克隆

原型模式中的浅克隆是指创建一个新对象时,只复制对象的基本数据类型引用类型的地址,而不复制引用类型所指向的对象。也就是说,新对象和原对象共享同一个引用类型对象。这种克隆方式相对较快,但可能会导致新对象和原对象之间的状态相互影响。

浅克隆的代码如下:

package com.pany.camp.design.principle;

import com.google.common.collect.Lists;
import lombok.Data;

import java.util.List;

/**
 *
 * @description:  浅克隆
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 8:19
 */
@Data
public class Prototype implements Cloneable {
    
    private String name;
    private int age;
    private List<String> hobbies;

    public Prototype(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new Prototype("张三", 18, Lists.newArrayList("篮球"));
        Prototype clonePrototype = prototype.clone();
        clonePrototype.setName("李四");
        clonePrototype.setAge(19);

        System.out.println(prototype == clonePrototype);
        System.out.println(prototype.getName() + ":" + prototype.getAge());
        System.out.println(clonePrototype.getName() + ":" + clonePrototype.getAge());
    }
}

Prototype 类实现了 Cloneable 接口,并重写了 clone() 方法。 clone() 方法使用了默认的浅克隆方式,即调用 super.clone() 方法进行复制。需要注意的是, hobbies 属性是一个引用类型,它所指向的对象并没有被复制,因此新对象和原对象共享同一个 hobbies 对象。如果需要避免这种情况,可以使用深克隆方式,即复制引用类型所指向的对象。

输出结果如下:

false
张三:18
李四:19

Process finished with exit code 0

浅克隆得到的对象和原对象不是同一个对象,它们是两个独立的对象。浅克隆只会复制原对象中基本数据类型和引用类型的地址,而不会复制引用类型指向的对象本身。因此,浅克隆得到的对象和原对象共享引用类型指向的对象,但是它们本身是不同的对象。

4、深克隆

原型模式中的深克隆是指创建一个新对象时,不仅复制对象的基本数据类型和引用类型的地址,还复制引用类型所指向的对象。也就是说,新对象和原对象拥有完全独立的引用类型对象,互相之间不会产生影响。这种克隆方式相对较慢,但可以确保新对象和原对象之间的状态相互独立。

代码如下:

package com.pany.camp.design.principle;

import com.google.common.collect.Lists;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @description:  深克隆
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-27 8:24
 */
@Data
public class Prototype1 implements Cloneable {

    private String name;
    private int age;
    private List<String> hobbies;

    public Prototype1(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    @Override
    public Prototype1 clone() throws CloneNotSupportedException {
        Prototype1 prototype = (Prototype1) super.clone();
        prototype.hobbies = new ArrayList<>(hobbies);
        return prototype;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype1 prototype = new Prototype1("张三", 18, Lists.newArrayList("篮球"));
        Prototype1 clonePrototype = prototype.clone();

        clonePrototype.setName("李四");
        clonePrototype.setAge(19);

        System.out.println(prototype == clonePrototype);
        System.out.println(prototype.getName() + ":" + prototype.getAge());
        System.out.println(clonePrototype.getName() + ":" + clonePrototype.getAge());
    }
}

输出结果如下:

false
张三:18
李四:19

Process finished with exit code 0

5、有几种实现深克隆的方式

实现深克隆的方式有以下几种:

  1. 重写 clone() 方法。如果要实现深克隆,需要在 clone() 方法中对所有引用类型的成员变量进行递归复制。

  2. 实现 Serializable 接口,通过序列化和反序列化实现深克隆。将对象序列化为字节数组,再将字节数组反序列化为新的对象。这种方式需要注意对象及其成员变量必须都是可序列化的。

  3. 使用第三方库,如 Apache Commons 中的 SerializationUtils 类、Spring Framework 中的 SerializationUtils 类或 Google 的 Protobuf 库等,这些库提供了方便的深克隆方法,可以直接使用。

  4. 递归克隆引用类型,引用类型也实现Cloneable接口,在源对象的克隆方法里,循环嵌套(或者可以说递归)克隆引用对象。

需要注意的是,以上方法都需要确保对象及其成员变量都是可序列化的,否则会抛出 NotSerializableException 异常。同时,深克隆的效率可能会比浅克隆低,因为需要递归复制所有引用类型的成员变量。

5.1、通过序列化的方式实现深克隆

代码如下:

package com.pany.camp.design.principle;

import lombok.Data;

import java.io.*;

/**
 *
 * @description:  通过序列化的方式实现深克隆
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 8:35
 */
@Data
public class DeepCopy implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private Address address;

    public DeepCopy(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public DeepCopy deepClone() {
        DeepCopy clone = null;
        try {
            // 将对象序列化为字节数组
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            oos.close();
            // 将字节数组反序列化为新的对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            clone = (DeepCopy) ois.readObject();
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return clone;
    }

    public static void main(String[] args) {
        Address address = new Address("China", "Beijing");
        DeepCopy original = new DeepCopy("Tom", 20, address);
        DeepCopy copy = original.deepClone();
        System.out.println(copy.getName()); // Tom
        System.out.println(copy.getAge()); // 20
        System.out.println(copy.getAddress().getCountry()); // China
        System.out.println(copy.getAddress().getCity()); // Beijing
    }
}

@Data
class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    private String country;
    private String city;

    public Address(String country, String city) {
        this.country = country;
        this.city = city;
    }
}

注意这种方式需要用到流,DeepCopy 类和 Address 类都实现了 Serializable 接口,这样才能被序列化和反序列化。在 deepClone() 方法中,将对象序列化为字节数组,再将字节数反序列化为新的对象。

输出结果如下:

Tom
20
China
Beijing

Process finished with exit code 0

5.3、使用 Apache Commons进行深克隆

代码如下:

package com.pany.camp.design.principle;

import org.apache.commons.lang.SerializationUtils;

import java.io.Serializable;

/**
 *
 * @description:  使用 Apache Commons进行深克隆
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 8:45
 */
public class DeepCopyExample {
    public static void main(String[] args) {
        Address2 address = new Address2("中国", "北京");
        Person original = new Person("Tom", 20, address);
        Person copy = (Person) SerializationUtils.clone(original);
        System.out.println(copy.getName()); // Tom
        System.out.println(copy.getAge()); // 20
        System.out.println(copy.getAddress().getCountry()); // 中国
        System.out.println(copy.getAddress().getCity()); // 北京
    }
}

class Person implements Serializable {
    private String name;
    private int age;
    private Address2 address;

    public Person(String name, int age, Address2 address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Address2 getAddress() {
        return address;
    }
}

class Address2 implements Serializable {
    private String country;
    private String city;

    public Address2(String country, String city) {
        this.country = country;
        this.city = city;
    }

    public String getCountry() {
        return country;
    }

    public String getCity() {
        return city;
    }
}

输出如下:

Tom
20
中国
北京

Process finished with exit code 0

我们使用了 Apache Commons 中的 SerializationUtils.clone() 方法进行深克隆。需要注意的是,被克隆的对象必须实现 Serializable 接口,否则会抛出 SerializationException 异常。

Apache Commons 中的深克隆方法还有其他的实现方式,例如使用 BeanUtils.cloneBean() 方法进行克隆等等。具体使用哪种方法,可以根据实际情况进行选择。

5.3、递归克隆引用

代码如下:

package com.pany.camp.design.principle;

import lombok.Data;

/**
 *
 * @description:  递归克隆引用
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 8:41
 */
@Data
public class DeepCopy1 implements Cloneable {
    private String name;
    private int age;
    private Address1 address;

    public DeepCopy1(String name, int age, Address1 address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public DeepCopy1 deepClone() {
        DeepCopy1 clone = null;
        try {
            clone = (DeepCopy1) super.clone();
            clone.address = address.deepClone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
    
    public static void main(String[] args) {
        Address1 address = new Address1("China", "Beijing");
        DeepCopy1 original = new DeepCopy1("Tom", 20, address);
        DeepCopy1 copy = original.deepClone();
        System.out.println(copy.getName()); // Tom
        System.out.println(copy.getAge()); // 20
        System.out.println(copy.getAddress().getCountry()); // China
        System.out.println(copy.getAddress().getCity()); // Beijing
    }

}

@Data
class Address1 implements Cloneable {

    private String country;
    private String city;

    public Address1(String country, String city) {
        this.country = country;
        this.city = city;
    }

    public Address1 deepClone() {
        Address1 clone = null;
        try {
            clone = (Address1) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

}

输出结果如下:

Tom
20
China
Beijing

Process finished with exit code 0

DeepCopy 类和 Address 类都实现了 Cloneable 接口,这样才能被克隆。在 deepClone() 方法中,首先调用 super.clone() 方法创建一个浅克隆的对象,然后对引用类型的成员变量 address 进行递归克隆。在 Address 类中,也要实现 Cloneable 接口,并在 deepClone() 方法中调用 super.clone() 方法创建一个浅克隆的对象。最后,在 main() 方法中测试深克隆的效果。

在这里插入图片描述

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/690752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32模拟I2C获取TCS34725光学颜色传感器数据

STM32模拟I2C获取TCS34725光学颜色传感器数据 TCS34725是RGB三色颜色传感器&#xff0c;和TCS34727都属于TCS3472系列&#xff0c;在电气特性上略有差别&#xff0c;TCS34727相比TCS34725在I2C总线的访问电平上可以更低&#xff0c;而在I2C软件访问地址方面则一致。 TCS3472内…

leetcode:1431. 拥有最多糖果的孩子(python3解法)

难度&#xff1a;简单 给你一个数组 candies 和一个整数 extraCandies &#xff0c;其中 candies[i] 代表第 i 个孩子拥有的糖果数目。 对每一个孩子&#xff0c;检查是否存在一种方案&#xff0c;将额外的 extraCandies 个糖果分配给孩子们之后&#xff0c;此孩子有 最多 的糖…

Spring Boot中的@RequestMapping注解,如何使用

Spring Boot中的RequestMapping注解 介绍 Spring Boot是一个流行的Java框架&#xff0c;它提供了许多方便的注解和工具&#xff0c;使得Web应用程序的开发变得更加容易。其中&#xff0c;RequestMapping注解是Spring Boot中最常用的注解之一&#xff0c;它可以帮助开发者定义…

django旅游推荐系统-计算机毕设 附源码82884

django旅游推荐系统 摘 要 随着社会的快速发展和人们生活水平的不断提高&#xff0c;旅游已逐渐成为人们生活的重要组成部分&#xff0c;用户能够获取旅游信息的渠道也随信息技术的广泛应用而增加。大量未经过滤的信息在展示给用户的同时&#xff0c;也淹没了用户真正感兴趣的信…

10个图像处理的Python库

在这篇文章中&#xff0c;我们将整理计算机视觉项目中常用的Python库&#xff0c;如果你想进入计算机视觉领域&#xff0c;可以先了解下本文介绍的库&#xff0c;这会对你的工作很有帮助。 1、PIL/Pillow Pillow是一个通用且用户友好的Python库&#xff0c;提供了丰富的函数集…

【MOOC 测验】第5章 链路层

1、局域网的协议结构一般不包括&#xff08; &#xff09; A. 数据链路层B. 网络层C. 物理层D. 介质访问控制层 逻辑链路控制子层、介质访问控制子层、物理层 2、下列关于二维奇偶校验的说法&#xff0c;正确的是&#xff08; &#xff09; A. 可以检测和纠正双比特差错B…

OV Image Sensor PLL设置

本文讨论OV的Image Sensor PLL的配置。 1.PLL的组成和功能 如图为OS08A10的框图&#xff0c;由图可知&#xff0c;Image Sensor其实是一个模数混合的电路&#xff0c;PLL提供了诸如ADC,gain control,MIPI,I2C等电路所用的时钟。 既然 Image Sensor的PLL是Image Senor非常重要…

详解Vue组件系统

Vue渲染的两大基础方式 new 一个Vue的实例 这个我们一般会使用在挂载根节点这一初始化操作上&#xff1a; new Vue({el: #app }) 复制 注册组件并使用 通过Vue.component&#xff08;&#xff09;去注册一个组件&#xff0c;你就可以全局地使用它了&#xff0c;具体体现在…

什么是信号槽机制,如何实现,有什么用?(Qt面试题)

1. 什么是信号槽机制&#xff1f; 信号槽机制&#xff08;Signal-Slot mechanism&#xff09;是一种在软件开发中常用的设计模式&#xff0c;用于实现对象间的通信和事件处理。该机制最初由Qt框架引入并广泛应用&#xff0c;后来也被其他编程框架和库所采用。 信号槽机制通过定…

这样做,轻松拿捏阻焊桥!

PCB表面的一层漆&#xff0c;称为阻焊油墨&#xff0c;也就是PCB线路板阻焊油墨。阻焊油墨是PCB线路板中非常常见、也是主要使用的油墨&#xff0c;一般90%都是绿色&#xff0c;但也有杂色油墨&#xff1a;红色、蓝色、黑色、白色、黄色等。 阻焊油墨的作用就是绝缘&#xff0…

postman持续集成-Jenkins手动构建

Jenkins启动 在jenkins.war文件所在的目录输入cmd打开终端输入: java -jar jenkins.war启动服务,启动后终端的窗口不要关闭 在浏览器地址栏输入:localhost:8080 准备工作 打开已完成并测试无误的postman项目脚本,再次执行测试 导出测试用例集和测试环境两个文件,注意全部…

【换根DP】CF1324F

Maximum White Subtree - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a; 思路&#xff1a; 先去树形DP求出DP值&#xff0c;这很好求 设dp[u]为以u为根的子树中白-黑的最大值 初始化就是&#xff1a;如果u本身是黑&#xff0c;那dp[u]-1&#xff0c;否则dp…

K8s(Kubernetes)学习(三):pod概念及相关操作

1 什么是 Pod 摘取官网: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/#working-with-pods 1.1 简介 Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。Pod&#xff08;就像在鲸鱼荚或者豌豆荚中&#xff09;是一组&#xff08;一个或多个&…

【Java面试题】Java基础——面向对象

文章目录 重载和重写的区别★★★Java的三大特性请说明一下Super关键字的作用&#xff1f;static关键字的作用&#xff1f;final关键字的作用&#xff1f;super关键字和this关键字的作用&#xff1f;面向对象的三大特性★★★成员变量和局部变量的区别&#xff1f;Java能实现多…

JMeter请求头添加删除方法(解决请求头类型冲突)

JMeter请求头添加删除方法&#xff08;解决请求头类型冲突&#xff09; 1. 为什么会有冲突 请求头的Content-Type类型在做上传和请求图片地址是&#xff0c;请求头类型是不一样的 请求图片地址&#xff1a;Content-Type: image/jpeg 一般的Restful接口&#xff1a;Content-Ty…

Linux使用第三方库链接的使用方式——静态式

目录 二.第三方库为静态库时&#xff1a; 方法1&#xff1a; 两个窗口去分别模拟两个窗口公司A(客户端)&#xff0c;公司B(服务端)的视角案例实现&#xff1a; 方法2——优化&#xff1a;该方法在上述方法1的第10步后开始进行&#xff1a; 这里强调一个问题&#xff1a; 今天…

linux上虚拟机vmware-workstation离线安装详细教程

linux上虚拟机vmware-workstation详细教程 一、VMWare基本介绍二、VMWare下载2.1 查看本地系统信息2.2 选择及下载合适的版本 三、VMWare安装3.1 安装依赖库3.2 vmware安装3.3 验证安装3.4 异常及解决方案3.4.1 Failed to start SYSV3.4.2 GLib does not have GSettings suppor…

编程:“上学时如果遇到自己,我会更早的成为我?”

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 如果当年我遇到自己&#xff0c;我会更早的成为我&#xff1f; 你觉得一瞬间的技术能力提升&#xff0c;是必须经历过过吃苦或者…

Python安装教程(初学者很实用)

一、Python环境搭建 1、下载Python 进入Python官网下载安装包 https://www.python.org/ 2、选择合适的版本&#xff0c;点击下载 3、安装Python 双击安装软件 等待安装完成 出现【setup was successful】&#xff0c;表示安装成功 4、检验是否安装成功 通过【winr】调出…

llama.cpp LLM模型 windows cpu安装部署

参考&#xff1a; https://www.listera.top/ji-xu-zhe-teng-xia-chinese-llama-alpaca/ https://blog.csdn.net/qq_38238956/article/details/130113599 cmake windows安装参考&#xff1a;https://blog.csdn.net/weixin_42357472/article/details/131314105 llama.cpp下载编…