当前位置: 首页 > news >正文

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设计模式 - 原型模式

简介 原型模式&#xff08;Prototype Pattern&#xff09;是用于创建重复的对象&#xff0c;同时又能保证性能。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式是实现了一个原型接口&#xff0c;该接口用于创建当前对象的克隆。当直…...

深度学习中的 “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系统的问题&#xff0c;他们自己也有ERP系统&#xff0c;但是总觉得好像还差了点什么&#xff0c;不知道是什么。今天&#xff0c;我想通过本文&#xff0c;来向您简要地阐述ERP与WMS系统在仓储管理上的不同之处。 ERP仓库是以财务为导向的&…...

一文吃透 Spring 中的IOC和DI

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

分布式任务处理: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服务实现不同服务&#xff0c;不同配置是通过RibbonClient和RibbonClients两个注解来实现的。RibbonClient注册的某个Client配置类。RibbonClients注册的全局默认配置类。 Feign实现不同服务&#xff0c;不同配置&#xff0c;是根据FeignClient来获取自定义的配置。 示…...

【webpack5】一些常见优化配置及原理介绍(二)

这里写目录标题介绍sourcemap定位报错热模块替换&#xff08;或热替换&#xff0c;HMR&#xff09;oneOf精准解析指定或排除编译开启缓存多进程打包移除未引用代码配置babel&#xff0c;减小代码体积代码分割&#xff08;Code Split&#xff09;介绍预获取/预加载(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学习笔记~ 从入门到精通!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、HTML1. 什么是HTML&#xff1f;一个完整的页面&#xff1a;<!DOCTYPE> 声明中文编码2.HTML基础①标签头部元素标题段落注释水平线文本格式化②属性3.H…...

LeetCode 430. 扁平化多级双向链表

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 你会得到一个双链表&#xff0c;其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表&#xff0c;也包含这些特殊的节点。这些子列表可以有一…...

2.5|iot|第1章嵌入式系统概论|操作系统概述|嵌入式操作系统

目录 第1章&#xff1a; 嵌入式系统概论 1.嵌入式系统发展史 2.嵌入式系统定义* 3.嵌入式系统特点* 4.嵌入式处理器的特点 5.嵌入式处理分类 6.嵌入式系统的应用领域及嵌入式系统的发展趋势 第8章&#xff1a;Linux内核配置 1.内核概述 2.内核代码结构 第1章&#xf…...

一文教会你使用ChatGPT画图

引言 当今,ChatGPT在各行各业都有着广泛的应用,其自然语言处理技术也日益成熟。ChatGPT是一种被广泛使用的技术,除了能够生成文本,ChatGPT还可以用于绘图,这为绘图技术的学习和应用带来了新的可能性。本文将介绍如何利用ChatGPT轻松绘制各种形状,为对绘图技术感兴趣的读…...

Java资料分享

随着Java开发的薪资越来越高&#xff0c;越来越多人开始学习 Java 。在众多编程语言中&#xff0c;Java学习难度还是偏高的&#xff0c;逻辑性也比较强&#xff0c;但是为什么还有那么多人要学Java呢&#xff1f;Java语言是目前流行的互联网等企业的开发语言&#xff0c;是市面…...

yum/vim工具的使用

yum 我们生活在互联网发达的时代&#xff0c;手机电脑也成为了我们生活的必须品&#xff0c;在你的脑海中是否有着这样的记忆碎片&#xff0c;在一个明媚的早上你下定决心准备发奋学习&#xff0c;“卸载”了你手机上的所有娱乐软件&#xff0c;一心向学&#xff01;可是到了下…...

内网渗透(三十九)之横向移动篇-pass the ticket 票据传递攻击(PTT)横向攻击

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...

Unity性能优化之纹理格式终极篇

知识早班车&#xff1a;1、当n大于1时&#xff0c;2的n次幂一定能被4整除&#xff1b;证明&#xff1a;2^n 2^2*2^(n-1) 4*2^(n-1)2、4的倍数不一定都是2的次幂&#xff1b;证明&#xff1a;4*3 12&#xff1b;12不是2的次幂3、Pixel&#xff08;像素&#xff09;是组成图片…...

【Spark分布式内存计算框架——Spark SQL】9. Dataset(下)RDD、DF与DS转换与面试题

5.3 RDD、DF与DS转换 实际项目开发中&#xff0c;常常需要对RDD、DataFrame及Dataset之间相互转换&#xff0c;其中要点就是Schema约束结构信息。 1&#xff09;、RDD转换DataFrame或者Dataset 转换DataFrame时&#xff0c;定义Schema信息&#xff0c;两种方式转换为Dataset时…...

Windows 环境下,cmake工程导入OpenCV库

目录 1、下载 OpenCV 库 2、配置环境变量 3、CmakeLists.txt 配置 1、下载 OpenCV 库 OpenCV官方下载地址&#xff1a;download | OpenCV 4.6.0 下载完毕后解压&#xff0c;便可以得到下面的文件 2、配置环境变量 我们需要添加两个环境变量&#xff0c;一个是 OpenCVConfi…...

微服务架构设计模式-(16)重构

绞杀者应用程序 由微服务组成的应用程序&#xff0c;将新功能作为服务&#xff0c;并逐步从单体应用中提取服务来实现。好处 尽早并频繁的体现价值 快速开发交付&#xff0c;使用 与之相对的是“一步到位”重构&#xff0c;这时间长&#xff0c;且期间有新的功能加入&#xff…...

.NET 7 AOT 使用及 .NET 与 Go 语言互操作详解

.NET 7 AOT 使用及 .NET 与 Go 语言互操作详解 目录 .NET 7 AOT 使用及 .NET 与 Go 语言互操作详解 一、背景与技术概述 1.1 AOT 编译技术简介 1.2 Go 语言与 .NET 的互补性 二、.NET 7 AOT 编译实践 2.1 环境准备 2.2 创建 AOT 项目 2.3 AOT 编译流程 2.4 调试信息处…...

湖北理元理律师事务所:债务优化中的“生活锚点”设计

在债务重组领域&#xff0c;一个常被忽视的核心矛盾是&#xff1a;还款能力与生存需求的冲突。过度压缩生活支出还债&#xff0c;可能导致收入中断&#xff1b;放任债务膨胀&#xff0c;又加剧精神压力。湖北理元理律师事务所通过“三步平衡法”&#xff0c;尝试在法理框架内破…...

muduo库的初步认识和基本使用,创建一个简单查询单词服务系统

小编在学习完muduo库之后&#xff0c;觉得对于初学者&#xff0c;muduo库还是有点不好理解&#xff0c;所以在此&#xff0c;小编来告诉大家muduo库的初步认识和基本使用&#xff0c;让初学者也可以更快的上手和使用muduo库。 Muduo由陈硕大佬开发&#xff0c;是⼀个基于 非阻塞…...

【leetcode】704. 二分查找

二分查找 题目代码 题目 704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,…...

物联网常用协议Modbus、CAN、BACnet介绍

一、Modbus Modbus 作为工业通信领域的基石,是一款被广泛应用的工业通信协议,主要用于实现可编程逻辑控制器(PLC)等工业电子设备之间的连接。1979 年,Modicon 公司(现施耐德电气的一部分)开发了这一协议,旨在简化工厂内设备间的通信流程。经过多年发展,Modbus 衍生出…...

前端开源JavaScrip库

以下内容仍在持续完善中&#xff0c;如有遗漏或需要补充之处&#xff0c;欢迎在评论区指出。感谢支持&#xff0c;如果觉得有帮助&#xff0c;欢迎点赞鼓励。感谢支持 JavaScript 框架Vue.jsVue.js - 渐进式 JavaScript 框架 | Vue.jsReactReactAngularHome • AngularjQueryj…...

Java面试实战:从Spring到大数据的全栈挑战

Java面试实战&#xff1a;从Spring到大数据的全栈挑战 在某家知名互联网大厂&#xff0c;严肃的面试官正在面试一位名叫谢飞机的程序员。谢飞机以其搞笑的回答和对Java技术栈的独特见解而闻名。 第一轮&#xff1a;Spring与微服务的探索 面试官&#xff1a;“请你谈谈Spring…...

图论核心:深度搜索DFS 与广度搜索BFS

一、深度优先搜索&#xff08;DFS&#xff09;&#xff1a;一条路走到黑的探索哲学 1. 算法核心思想 DFS&#xff08;Depth-First Search&#xff09;遵循 “深度优先” 原则&#xff0c;从起始节点出发&#xff0c;尽可能深入地访问每个分支&#xff0c;直到无法继续时回溯&a…...

SpringBoot:统一功能处理、拦截器、适配器模式

文章目录 拦截器什么是拦截器&#xff1f;为什么要使用拦截器&#xff1f;拦截器的使用拦截路径执行流程典型应用场景DispatcherServlet源码分析 适配器模式适配器模式定义适配器模式角色适配器模式的实现适配器模式应用场景 统⼀数据返回格式优点 统一处理异常总结 拦截器 什…...

XCTF-web-file_include

解析 <?php highlight_file(__FILE__); // 高亮显示当前PHP文件源代码 include("./check.php"); // 包含检查文件&#xff08;可能包含安全过滤逻辑&#xff09;if(isset($_GET[filename])) { // 检查是否传入filename参数$filename $_GET[f…...