【Java笔记+踩坑】设计模式——原型模式
导航:
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/黑马旅游/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码-CSDN博客
目录
零、经典的克隆羊问题(复制10只属性相同的羊)
一、传统方案:循环new对象
1.1 实现方案
1.2 优缺点和改进思路
二、原型模式(Prototype模式)
2.1 基本介绍
2.2 原理
2.2.1 UML图:原型接口、具体原型类、客户端代码
2.2.2 代码演示
2.3 原型模式解决克隆羊问题
2.4 优缺点和使用场景
2.4.1 优点
2.4.2 缺点
2.4.3 适用场景
三、扩展
3.1 Spring源码中的原型模式:ApplicationContext类的getBean()方法
3.2 浅拷贝和深拷贝
3.2.1 浅拷贝:引用类型变量拷贝引用
3.2.2 深拷贝:引用类型变量拷贝值
零、经典的克隆羊问题(复制10只属性相同的羊)
问题描述:现在有一只羊,姓名为 Tom,年龄为 1,颜色为白色,请编写程序创建和 Tom 羊属性完全相同的 10 只羊。
一、传统方案:循环new对象
1.1 实现方案
羊类:
public class Sheep {private String name;private Integer age;public Sheep(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}
克隆羊:
public class Client {public static void main(String[] args) {for (int i = 0; i < 10; i++) {Sheep sheep = new Sheep("Tom", 1, "白色");System.out.println(sheep);}}
}
1.2 优缺点和改进思路
传统方法优点
- 好理解,简单易操作
缺点:
- 每次获取再复制效率低:在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
- 不灵活:总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
改进的思路分析:Object 类的 clone() 方法
Object 类是所有类的根类,Object 类提供了一个 clone 方法,该方法可以将一个 Java 对象复制一份,但是对应的类必须实现Cloneable接口,该接口表示该类能够复制且具有复制的能力 ==> 原型模式
二、原型模式(Prototype模式)
2.1 基本介绍
原型模式(Prototype 模式):用原型实例指定创建对象种类,并通过拷贝原型创建新的对象
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
原理:将一个原型对象传给要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
形象的理解:孙大圣拔出猴毛,变出其它孙大圣
创建型设计模式:关注如何有效地创建对象,以满足不同的需求和情境。
包括:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
2.2 原理
2.2.1 UML图:原型接口、具体原型类、客户端代码
- Prototype:原型类。包含一个用于复制对象的克隆方法。可以使用Cloneable接口作为原型接口。
- ConcretePrototype:具体原型类。实现原型接口、重写克隆方法clone()的具体类。
- Client:让一个原型对象克隆自己,创建一个属性相同的对象
2.2.2 代码演示
原型接口: 可以是Cloneable接口也可以是自定义带clone()方法的接口
// 步骤1:定义原型接口
interface Prototype extends Cloneable {Prototype clone();
}
具体原型类:
// 步骤2:实现具体原型类
class ConcretePrototype implements Prototype {@Overridepublic Prototype clone() {try {return (Prototype) super.clone(); // 使用浅拷贝} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}
客户端代码: 通过clone()方法创建原型对象
// 步骤3:客户端代码
public class Client {public static void main(String[] args) {
//创建具体类对象ConcretePrototype prototype = new ConcretePrototype();
//通过clone方法创建对象ConcretePrototype clonedObject = (ConcretePrototype) prototype.clone();}
}
2.3 原型模式解决克隆羊问题
问题回顾:
现在有一只羊,姓名为 Tom,年龄为 1,颜色为白色,请编写程序创建和 Tom 羊属性完全相同的 10 只羊。
UML 类图
原型接口:Cloneable接口。
具体原型类:实现Cloneable接口
@Data
public class Sheep implements Cloneable {private String name;private Integer age;private String color;public Sheep(String name, Integer age, String color) {this.name = name;this.age = age;this.color = color;}@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep) super.clone();} catch (Exception e) {e.printStackTrace();}return sheep;}
}
客户端: 调用具体原型类的clone()方法创建10个对象
public class Client {public static void main(String[] args) {Sheep sheep = new Sheep("Tom", 1, "白色");for (int i = 0; i < 10; i++) {Sheep sheep1 = (Sheep) sheep.clone();System.out.println(sheep1);}}
}
2.4 优缺点和使用场景
2.4.1 优点
- 构造方法复杂时开销小:如果构造函数的逻辑很复杂,此时通过new创建该对象会比较耗时,那么就可以尝试使用克隆来生成对象。
- 运行时动态创建对象:不用重新初始化对象,而是动态地获得对象运行时的状态
- 开闭原则(OCP原则):如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需更改客户端代码。相反,如果使用new方式,就需要在客户端修改构造参数。这使得系统更加灵活和可维护。
- 对象封装性:原型模式可以帮助保护对象的封装性,因为客户端代码无需了解对象的内部结构,只需知道如何克隆对象。
- 多态性:原型模式支持多态性,因为克隆操作可以返回具体子类的对象,而客户端代码不需要关心对象的具体类。
扩展:
开闭原则(OCP原则):代码对修改关闭,对扩展开放。
2.4.2 缺点
- 构造方法简单时开销大:如果构造函数的逻辑很简单,原型模式的效率不如new,因为JVM对new做了相应的性能优化。
-
//验证构造方法简单时,原型模式开销大long startTime = System.currentTimeMillis();Student student = new Student();//克隆循环十万次for (int i = 0; i < 100000; i++) {student.clone();}long midTime = System.currentTimeMillis(); //20msSystem.out.println("克隆生成对象耗费的时间:" + (midTime - startTime) + "ms");//new10万次for (int i = 0; i < 100000; i++) {new Student();} //5msSystem.out.println("new生成对象耗费的时间:" + (System.currentTimeMillis() - midTime) + "ms");
- 要注意深拷贝和浅拷贝问题:实现Cloneable接口时,如果具体原型类直接返回super.clone(),则是浅拷贝。克隆的对象里,引用类型变量只拷贝引用,依然指向旧的地址。
- 代码复杂性:在实现深拷贝的时候可能需要比较复杂的代码。设计模式一般都是以代码复杂性为代价,提高可扩展性、可读性。
2.4.3 适用场景
- 构造方法复杂:要创建的对象构造方法逻辑很复杂,即创建新的对象比较复杂时,使用原型模式会比直接new效率更高;
- 经常需要克隆:经常要创建一个和原对象属性相同的对象时,可以考虑原型模式。
三、扩展
3.1 Spring源码中的原型模式:ApplicationContext类的getBean()方法
Spring 框架Bean的生命周期中,ApplicationContext类的getBean()方法中,有用到原型模式。
获取Bean时会判断配置的Bean是单例还是原型,如果是原型,则用原型模式创建Bean。
验证:bean指定原型模式后,getBean()获取到的多个Bean是不同对象。
@Component("id01")
@Scope("prototype")
public class Monster {private String name;private int health;public Monster() {// 默认构造函数}public Monster(String name, int health) {this.name = name;this.health = health;}// 添加其他属性和方法
}
也可以用xml形式注册Bean:
<!-- 这里我们使用scope="prototype"即 原型模式来创建 --> <bean id="id01" class="com.atquigu.spring.bean.Monster" scope="prototype"/> </beans>
测试:
public class ProtoType {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");// 通过ID获取MonsterObject bean = applicationContext.getBean("id01");System.out.println("bean: " + bean); // 输出“牛魔王"....Object bean2 = applicationContext.getBean("id01");System.out.println("bean2: " + bean2); // 输出“牛魔王"....System.out.println(bean == bean2); // false}
}
注解方式是@Scope("prototype")。
回顾:
手写Spring源码(简化版)-CSDN博客
3.2 浅拷贝和深拷贝
3.2.1 浅拷贝:引用类型变量拷贝引用
- 浅拷贝:拷贝后对象是新地址,基本类型变量拷贝值,引用类型变量拷贝引用。只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存。
- 深拷贝:拷贝后对象是新地址,基本类型变量拷贝值,引用类型变量拷贝克隆后的值。创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。反序列化创建对象是深拷贝。
实现方案:具体原型类直接返回super.clone()
实现Cloneable 接口,重写 clone()方法, 直接返回super.clone()。
public class Person implements Cloneable { //虽然clone()是Object类的方法,但Java规定必须得实现一下这个接口public int age;//基本类型变量拷贝值public Person(int age) {this.age = age;}@Overridepublic Person clone() {try {return (Person) super.clone();} catch (CloneNotSupportedException e) {return null;}}public static void main(String[] args) {Person p1 = new Person(18);Person p2 = p1.clone(); //p2将是p1浅拷贝的对象p2.age = 20;System.out.println(p1 == p2); // false。拷贝后对象是新地址System.out.println(p1.age); // 18。基本类型变量拷贝值}
}
3.2.2 深拷贝:引用类型变量拷贝值
深拷贝:拷贝后对象是新地址,基本类型变量拷贝值,引用类型变量拷贝克隆后的值。创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。反序列化创建对象是深拷贝。
实现方案:具体原型类专门克隆引用类型变量
实现Cloneable 接口,重写 clone()方法, 给super.clone()的引用类型成员变量也clone()一下,然后再返回克隆的对象。
public class Person implements Cloneable {public int age;//基本类型变量拷贝值public int[] arr = new int[] {1, 2, 3};public Person(int age) {this.age = age;}@Overridepublic Person clone() {try {Person person = (Person) super.clone();person.arr = this.arr.clone(); // 用引用类型的 clone 方法,引用类型变量拷贝克隆后的值return person;} catch (CloneNotSupportedException e) {return null;}}
}
相关文章:

【Java笔记+踩坑】设计模式——原型模式
导航: 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 目录 零、经典的克隆羊问题(复制10只属性相同的羊) 一、传统方案࿱…...

Flutter GetX使用详解
介绍 GetX是一款功能强大且轻量级的Flutter状态管理和路由管理库。它提供了一种简单而强大的方式来构建Flutter应用程序,无需大量的模板代码。GetX不仅提供了状态管理和路由管理,还包括其他实用工具,如国际化和依赖注入。 在本文中…...

【ARM Coresight 系列文章 3.3 - ARM Coresight SWD 协议详细介绍】
文章目录 1.1 SWD 协议框图1.2 读/写时序及命令1.2.1 SWD 时序1.2.2 SWD 命令详情1.3 芯片探测1.3.1 获取芯片 ID1.4 读/写操作1.1 SWD 协议框图 SWD协议可以配置SoC内部几乎所有的寄存器。时钟信号由SWCLK 管脚输入,数据信号从SWDIO管脚输入输出。首先 HOST 对SW-DP 进行操作…...

作为开发者,可视化开发工具了解一下
你是否为编程世界的各种挑战感到头痛?想要以更高效、简单的方式开发出专业级的项目? JNPF低代码工具正是你苦心寻找的产品!它是一款专为稍微懂一点点编程思想的入门级人员设计的神奇工具,集成了丰富的功能和组件,让你轻…...

Python:实现日历功能
背景 日常生活中,每天都要用到日历,日历成为我们生活中的必需品,那么如何制作日历呢,其实方法有很多,可以直接在excel中制作,也可以手画等等。 学习过编程的朋友,能否想到用Python编写一…...

2.9.C++项目:网络版五子棋对战之业务处理模块的设计
文章目录 一、意义二、功能三、管理(一)客户端请求(二)websocket 四、框架五、完整代码 一、意义 将所有的模块整合在一起,通过网络通信获取到客户端的请求,提供不同的业务处理。 服务器模块,是…...

springboot actuator 常用接口
概述 微服务作为一项在云中部署应用和服务的新技术是当下比较热门话题,而微服务的特点决定了功能模块的部署是分布式的,运行在不同的机器上相互通过服务调用进行交互,业务流会经过多个微服务的处理和传递,在这种框架下࿰…...

知识点滴 - Email地址不区分大小写
电子邮件地址本身对字符大小写不敏感。这意味着实际的电子邮件地址,如 "exampleemail.com",并不区分字母的大小写。无论你输入的是大写字母还是小写字母,它仍然会到达同一个电子邮件账户。例如,如果您的电子邮件地址是 …...

同一个页面同一区域两个el-table在v-if下样式重叠问题
🍉正常情况下在radio切换时两个表格的样式应如下 🍉实际上用v-if显示时会出现以下问题(本该属于时间段相同模块的表格却出现在时间段自定义的表格中) 🍉解决方案: 🍃一、将v-if替换成v-show(…...

ExoPlayer架构详解与源码分析(6)——MediaPeriod
系列文章目录 ExoPlayer架构详解与源码分析(1)——前言 ExoPlayer架构详解与源码分析(2)——Player ExoPlayer架构详解与源码分析(3)——Timeline ExoPlayer架构详解与源码分析(4)—…...

【开题报告】基于Spring Boot的课程在线预约系统的设计与实现
1.引言 随着互联网的发展,线上教育和课程培训变得越来越普遍。然而,很多学生在选择课程时面临一些困扰,例如如何找到适合自己的课程,如何与老师进行预约等。因此,设计一个基于Spring Boot的课程在线预约系统具有重要的…...

React Hooks还有哪些常用的用法?
除了之前提到的 useState、useEffect、useContext、useRef、useMemo 和 useCallback,还有一些其他常用的 React Hooks,它们提供了额外的功能和灵活性。以下是其中一些常见的 React Hooks: 1:useReducer:用于在函数组件中管理复杂的状态逻辑,类似于 Redux 的 reducer。 …...

基于Java的学生学籍管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding) 代码参考数据库参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

Java内部类、匿名内部类、嵌套类详解
CONTENTS 1. 创建内部类2. 内部类到外部类的连接3. 在内部类中生成外部类对象的引用4. 匿名内部类5. 嵌套类6. 接口中的类 1. 创建内部类 创建内部类的方式就是把类定义放在一个包围它的类之中: package com.yyj;public class Parcel1 {class Contests {private i…...

【兔子王赠书第3期】《案例学Python(进阶篇)》
文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展,Python将会得到越来越多开发者的喜爱和应用。因为Python语法简单,学习速度快,大家可以用更短的时间掌握这门语…...

【C刷题】day6
一、选择题 1、以下叙述中正确的是( ) A: 只能在循环体内和switch语句体内使用break语句 B: 当break出现在循环体中的switch语句体内时,其作用是跳出该switch语句体,并中止循环体的执行 C: continue语句的作用是:在…...

MySQL精髓:如何使用ALL一次找到最大值
题目来自LeetCode 题目 表:Project -------------------- | Column Name | Type | -------------------- | project_id | int | | employee_id | int | -------------------- (project_id, employee_id) 是该表的主键(具有唯一值的列的组合)。 employee_id 是该表…...

安全设备
一.防火墙 5层应用层 防火墙 4层 udp tcp 协议 华为 厂商 华为 h3 1.区域划分 Dmz 停火区 Untrust 不安全区域 Trust 安全区域 防火墙 默认禁止所有 二.Waf Web 应用防火墙 放到web前面 产品 雷池 绿盟 软件 安…...

基于Java的足球赛会管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding) 代码参考数据库参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

如何确定Apache Kafka的大小和规模
调整或扩展Kafka以获得最佳成本和性能的第一步是了解数据流平台如何使用资源。这里给一些实用的建议。 实现Apache Kafka的团队,或者扩展他们对强大的开源分布式事件流平台的使用,通常需要帮助理解如何根据他们的需求正确地调整和扩展Kafka资源。这可能…...

项目总结-新增商品-Pagehelper插件分页查询
(1)新增商品 工具类: /** * Title: FileUtils.java * Package com.qfedu.common.utils * Description: TODO(用一句话描述该文件做什么) * author Feri * date 2018年5月29日 * version V1.0 */ package com.gdsdxy.common.u…...

java基础篇-环境变量
java基础 编程学习的关键点、重点1.环境变量设置待续 编程学习的关键点、重点 输入输出 Java语言、C语言、Python语言、甚至SQL语言,都需要实战、做大量输入输出等 1.环境变量设置 1.下载jdk安装 jdk官网下载直达链接:https://www.oracle.com/java/te…...

API自动化测试:如何构建高效的测试流程!
一、引言 在当前的软件开发环境中,API(Application Programming Interface)扮演了极为重要的角色,连接着应用的各个部分。对API进行自动化测试能够提高测试效率,降低错误,确保软件产品的质量。本文将通过实…...

MySQL8锁的问题
关键字 mysql 8、lock 问题描述 项目上反馈,一个简单的提交操作需要 40 秒。 抓取 SQL 发现 update gl_credit_bill set verifystate2 where id2761279790403840 执行耗时近40秒解决问题思路 手动执行 SQL,发现非常快,基本排除数据库本身…...

进阶JAVA篇-深入了解 Stream 流对象的创建与中间方法、终结方法
目录 1.0 Stream 流的说明 2.0 Stream 流对象的创建 2.1 对于 Collection 系列集合创建 Stream 流对象的方式 2.2 对于 Map 系列集合创建 Stream 流对象的方式 2.3 对于数组创建 Stream 流对象的方式 3.0 Stream 流的中间方法 3.1 Stream 流的 filter() 中间方法 3.2 Stream 流…...

原型制作的软件 Experience Design mac( XD ) 中文版软件特色
XD是一个直观、功能强大的UI/UX开发工具,旨在设计、原型、用户之间共享材料以及通过数字技术进行设计交互。Adobe XD提供了开发网站、应用程序、语音界面、游戏界面、电子邮件模板等所需的一切。xd mac软件特色 体验设计的未来。 使用 Adobe XD 中快速直观、即取即…...

Kotlin中使用ViewBinding绑定控件并添加点击事件
文章目录 效果1、加入依赖2、与控件进行绑定在 Activity 中使用视图绑定 3、监听控件 效果 实现源码 class MainActivity : AppCompatActivity() {lateinit var binding:ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstan…...

Node学习笔记之MongoDB
一、简介 1.1 Mongodb 是什么 MongoDB 是一个基于分布式文件存储的数据库,官方地址 MongoDB: The Developer Data Platform | MongoDB 1.2 为什么选择 Mongodb 操作语法与 JavaScript 类似,容易上手,学习成本低 二、核心概念 Mongodb 中…...

awtk用C语言开发串口通信示例
awtk开发工具封装了串口,可以方便的异步调用,就做个程序试一下吧 在deepinlinux20.9版本调试通过,开始第一步先给系统增加usb串口线驱动 https://download.csdn.net/download/qiaozhangchi/87463972 串口控件ide里没有,需要自己…...

CICD 流程学习(五)Jenkins后端工程构建
案例1:数据库服务部署 MySQL部署 #安装MySQL服务 [rootServices ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootServices ~]# yum -y install mysql.x86_64 mysql-server.x86_64 mysql-devel.x86_64 ... Complete! [rootServices ~]# #启动…...