当前位置: 首页 > 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…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...