Java设计模式 - 原型模式
简介
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
克隆
原型模式对某个对象进行克隆,在最原始的古老方法就是通过new出实例,使用与之相同的参数,但是每次创建新的对象时候需要重新获取一下属性,效率不佳。 例子如下:
①、古老办法
本次使用lombok,省的还需要写get,set,toString;坐标导入如下:
<dependencies><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency>
</dependencies>
实体类,使用lombok的@Data注解
package com.lyd.demo.entity;
import lombok.Data;
/*** @Author: lyd* @Description:* @Date: 2022-08-25*/
@Data
public class Car {private String type;private String color;public Car(String type, String color) {this.type = type;this.color = color;}
}
使用古老办法,使用new一个一个赋值,很明显有些缺点,每次都需要调用对象去获取属性
package com.lyd.demo.ancient;
import com.lyd.demo.entity.Car;
/*** @Author: lyd* @Description:* @Date: 2022-08-25*/
public class Test {public static void main(String[] args) {Car car = new Car("奔驰", "black");// 复制更多对象Car c1 = new Car(car.getType(), car.getColor());Car c2 = new Car(car.getType(), car.getColor());Car c3 = new Car(car.getType(), car.getColor());System.out.println("car: " + car + " hashcode: " + car.hashCode());System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode());System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode());System.out.println("c3: " + c3 + " hashcode: " + c3.hashCode());}
}
运行结果:可见虽然是复制成功了,但是效率明显很低。

②、原型模式
用原型实例指定创建对象的种类,通过拷贝实例创建新的对象。 例子如下代码: 需要让car实现Cloneable并且实现clone方法。
package com.lyd.demo.entity;
import lombok.Data;
/*** @Author: lyd* @Description:* @Date: 2022-08-25*/
@Data
public class Car implements Cloneable {private String type;private String color;public Car(String type, String color) {this.type = type;this.color = color;}// 克隆实例@Overrideprotected Object clone() throws CloneNotSupportedException {Car car = null;car = (Car) super.clone();return car;}
}
测试:
package com.lyd.demo.entity;
/*** @Author: lyd* @Description:* @Date: 2022-08-25*/
public class PrototypeTest {public static void main(String[] args) throws CloneNotSupportedException {Car car = new Car("红旗", "red");Car c1 = (Car) car.clone();Car c2 = (Car) car.clone();Car c3 = (Car) car.clone();System.out.println("car: " + car + " hashcode: " + car.hashCode());System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode());System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode());System.out.println("c3: " + c3 + " hashcode: " + c3.hashCode());}
}
运行结果:

定义一个原型类,声明出克隆自己的接口。克隆的时候只要调用clone方法就可以实现对象克隆。
浅拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性复制一份给新的对象。因为是两份不同的数据,所以对其中一的对象的成员变量值进行修改,不会影响另一个对象拷贝得到的数据。 对于数据类型是引用类型的成员变量,比如说成员变量是某个数组,某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用指(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

在Car类中加一个属性
public Car NewEnergy;
编写测试浅拷贝类
package com.lyd.demo.entity;
/*** @Author: lyd* @Description: 浅拷贝* @Date: 2022-08-25*/
public class ShallowCopyTest {public static void main(String[] args) throws CloneNotSupportedException {Car car = new Car("红旗", "red");car.NewEnergy = new Car("奔驰", "blue");// 克隆Car c1 = (Car) car.clone();Car c2 = (Car) car.clone();Car c3 = (Car) car.clone();System.out.println("car: " + car + " hashcode: " + car.hashCode() + " NewEnergy: " + car.NewEnergy + " hashcode: " + car.NewEnergy.hashCode());System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode() + " NewEnergy: " + c1.NewEnergy + " hashcode: " + c1.NewEnergy.hashCode());System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode() + " NewEnergy: " + c2.NewEnergy + " hashcode: " + c2.NewEnergy.hashCode());System.out.println("c3: " + c3 + " hashcode: " + c3.hashCode() + " NewEnergy: " + c3.NewEnergy + " hashcode: " + c3.NewEnergy.hashCode());}
}
测试结果

可见,如果是基本数据类型,那就会传递值,将值赋给新的对象;对于对象,则只是进行引用传递。这种就是浅拷贝。这样会导致只要修改其中一个变量,就会导致所有都修改,例如 c2.NewEnergy.setColor("red"); 之后在打印一次数据。

深拷贝
深拷贝复制变量值,对于引用数据,则递归至基本类型后,再复制。深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。深拷贝相比于浅拷贝速度较慢并且花销较大。 重新定义两个实体类,其中一个只有基本数据类型,另一个包含引用数据类型
方式一:通过重写clone()方法
Car: 在克隆的时候直接调用super.clone()来完成对基本数据类型的克隆,通过引用数据类的clone方法在进行类型强制装换即可。
package com.lyd.demo.deep;
import lombok.Data;
import java.io.Serializable;
/*** @Author: lyd* @Description: 汽车类* @Date: 2022-08-25*/
@Data
public class Car implements Cloneable, Serializable {private String type;private String color;public NewEnergy newEnergy; // 引用数据类型public Car(String type, String color) {this.type = type;this.color = color;}// 克隆实例@Overrideprotected Object clone() throws CloneNotSupportedException {Object object = null;// 完成基本数据的装换object = super.clone();// 对引用数据类型的属性,单独处理Car car = (Car) object;car.newEnergy = (NewEnergy) newEnergy.clone();return car;}
}
NewEnergy:
package com.lyd.demo.deep;
import lombok.Data;
import java.io.Serializable;
/*** @Author: lyd* @Description: 新能源类* @Date: 2022-08-25*/
@Data
public class NewEnergy implements Serializable, Cloneable {private String type;private String color;public NewEnergy(String type, String color) {this.type = type;this.color = color;}/*** 该类都是基本数据类型,直接返回即可* @return* @throws CloneNotSupportedException*/@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
测试:
package com.lyd.demo.deep;
/*** @Author: lyd* @Description: 深拷贝* @Date: 2022-08-25*/
public class DeepCopyTest {public static void main(String[] args) throws CloneNotSupportedException {Car car = new Car("路虎", "green");car.newEnergy = new NewEnergy("路虎新能源", "blue");Car c1 = (Car) car.clone();Car c2 = (Car) car.clone();System.out.println("car: " + car + " hashcode: " + car.hashCode() + " NewEnergy: " + car.newEnergy + " hashcode: " + car.newEnergy.hashCode());System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode() + " NewEnergy: " + c1.newEnergy + " hashcode: " + c1.newEnergy.hashCode());System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode() + " NewEnergy: " + c2.newEnergy + " hashcode: " + c2.newEnergy.hashCode());System.out.println("==============================================================================================================================");c1.newEnergy.setColor("red"); // 修改System.out.println("car: " + car + " hashcode: " + car.hashCode() + " NewEnergy: " + car.newEnergy + " hashcode: " + car.newEnergy.hashCode());System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode() + " NewEnergy: " + c1.newEnergy + " hashcode: " + c1.newEnergy.hashCode());System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode() + " NewEnergy: " + c2.newEnergy + " hashcode: " + c2.newEnergy.hashCode());}
}
运行结果:

可见,不但完成了把引用对象进行拷贝,修改c1的属性时,不会影响其他的对象,这就是深拷贝
方式二:通过对象序列化
通过对象流的方式将对象进行序列化之后在进行反序列化完成深拷贝。这样的效果会更好。 只要在Car类中添加一个deepClone方法
// 方式二:实现序列化
public Object deepClone() {// 创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {// 序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); // 把当前对象以对象流的方式输出//反序列bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);Car car = (Car) ois.readObject();return car;} catch (Exception e) {e.printStackTrace();return null;} finally {// 关闭流try {ois.close();bis.close();oos.close();bos.close();} catch (IOException e) {e.printStackTrace();}}
}
测试:只要调用deepClone方法就可以
Car c1 = (Car) car.deepClone();
Car c2 = (Car) car.deepClone();
最后实验结果:

💓小建议:
理解设计模式不是一件简单的事情,需要不断的学习和动手去练习,才能理解。只有掌握好设计模式,才能够真正的理解SpringAOP和Mybatis的底层原理。各位读者可以和我一样,动手敲一敲代码,甚至用不同的例子来做,通过debug一步一步调试,还有就是多看看别人的例子。能够有助于理解!
相关文章:
Java设计模式 - 原型模式
简介 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直…...
深度学习中的 “Hello World“
Here’s an interesting fact—Each month, there are 186.000 Google searches for the keyword “deep learning.” 大家好✨,这里是bio🦖。每月有超18万的人使用谷歌搜索深度学习这一关键词,是什么让人们对深度学习如此感兴趣?接下来请跟随我来揭开深度学习的神秘面纱。…...
购买WMS系统前,有搞清楚与ERP仓库模块的区别吗
经常有朋友在后台询问我们关于WMS系统的问题,他们自己也有ERP系统,但是总觉得好像还差了点什么,不知道是什么。今天,我想通过本文,来向您简要地阐述ERP与WMS系统在仓储管理上的不同之处。 ERP仓库是以财务为导向的&…...
一文吃透 Spring 中的IOC和DI
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
分布式任务处理:XXL-JOB分布式任务调度框架
文章目录1.业务场景与任务调度2.任务调度的基本实现2.1 多线程方式实现2.2 Timer方式实现2.3 ScheduledExecutor方式实现2.4 第三方Quartz方式实现3.分布式任务调度4.XXL-JOB介绍5.搭建XXL-JOB —— 调度中心5.1 下载与查看XXL-JOB5.2 创建数据库表5.3 修改默认的配置信息5.4 启…...
【源码解析】Ribbon和Feign实现不同服务不同的配置
Ribbon服务实现不同服务,不同配置是通过RibbonClient和RibbonClients两个注解来实现的。RibbonClient注册的某个Client配置类。RibbonClients注册的全局默认配置类。 Feign实现不同服务,不同配置,是根据FeignClient来获取自定义的配置。 示…...
【webpack5】一些常见优化配置及原理介绍(二)
这里写目录标题介绍sourcemap定位报错热模块替换(或热替换,HMR)oneOf精准解析指定或排除编译开启缓存多进程打包移除未引用代码配置babel,减小代码体积代码分割(Code Split)介绍预获取/预加载(prefetch/pre…...
力扣sql简单篇练习(十九)
力扣sql简单篇练习(十九) 1 查询结果的质量和占比 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 用count是不会统计为null的数据的 SELECT query_name,ROUND(AVG(rating/position),2) quality,ROUND(count(IF(rating<3,rating,null))/count(r…...
线段树c++
前言 在谈论到种种算法知识与数据结构的时候,线段树无疑总是与“简单”和“平常”联系起来的。而这些特征意味着,线段树作为一种常用的数据结构,有常用性,基础性和易用性等诸多特点。因此,今天我来讲一讲关于线段树的话题。 定义 首先,线段树是一棵“树”,而且是一棵…...
HTML+CSS+JavaScript学习笔记~ 从入门到精通!
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、HTML1. 什么是HTML?一个完整的页面:<!DOCTYPE> 声明中文编码2.HTML基础①标签头部元素标题段落注释水平线文本格式化②属性3.H…...
LeetCode 430. 扁平化多级双向链表
原题链接 难度:middle\color{orange}{middle}middle 题目描述 你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一…...
2.5|iot|第1章嵌入式系统概论|操作系统概述|嵌入式操作系统
目录 第1章: 嵌入式系统概论 1.嵌入式系统发展史 2.嵌入式系统定义* 3.嵌入式系统特点* 4.嵌入式处理器的特点 5.嵌入式处理分类 6.嵌入式系统的应用领域及嵌入式系统的发展趋势 第8章:Linux内核配置 1.内核概述 2.内核代码结构 第1章…...
一文教会你使用ChatGPT画图
引言 当今,ChatGPT在各行各业都有着广泛的应用,其自然语言处理技术也日益成熟。ChatGPT是一种被广泛使用的技术,除了能够生成文本,ChatGPT还可以用于绘图,这为绘图技术的学习和应用带来了新的可能性。本文将介绍如何利用ChatGPT轻松绘制各种形状,为对绘图技术感兴趣的读…...
Java资料分享
随着Java开发的薪资越来越高,越来越多人开始学习 Java 。在众多编程语言中,Java学习难度还是偏高的,逻辑性也比较强,但是为什么还有那么多人要学Java呢?Java语言是目前流行的互联网等企业的开发语言,是市面…...
yum/vim工具的使用
yum 我们生活在互联网发达的时代,手机电脑也成为了我们生活的必须品,在你的脑海中是否有着这样的记忆碎片,在一个明媚的早上你下定决心准备发奋学习,“卸载”了你手机上的所有娱乐软件,一心向学!可是到了下…...
内网渗透(三十九)之横向移动篇-pass the ticket 票据传递攻击(PTT)横向攻击
系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...
Unity性能优化之纹理格式终极篇
知识早班车:1、当n大于1时,2的n次幂一定能被4整除;证明:2^n 2^2*2^(n-1) 4*2^(n-1)2、4的倍数不一定都是2的次幂;证明:4*3 12;12不是2的次幂3、Pixel(像素)是组成图片…...
【Spark分布式内存计算框架——Spark SQL】9. Dataset(下)RDD、DF与DS转换与面试题
5.3 RDD、DF与DS转换 实际项目开发中,常常需要对RDD、DataFrame及Dataset之间相互转换,其中要点就是Schema约束结构信息。 1)、RDD转换DataFrame或者Dataset 转换DataFrame时,定义Schema信息,两种方式转换为Dataset时…...
Windows 环境下,cmake工程导入OpenCV库
目录 1、下载 OpenCV 库 2、配置环境变量 3、CmakeLists.txt 配置 1、下载 OpenCV 库 OpenCV官方下载地址:download | OpenCV 4.6.0 下载完毕后解压,便可以得到下面的文件 2、配置环境变量 我们需要添加两个环境变量,一个是 OpenCVConfi…...
微服务架构设计模式-(16)重构
绞杀者应用程序 由微服务组成的应用程序,将新功能作为服务,并逐步从单体应用中提取服务来实现。好处 尽早并频繁的体现价值 快速开发交付,使用 与之相对的是“一步到位”重构,这时间长,且期间有新的功能加入ÿ…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
