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

【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 适用场景

  1. 构造方法复杂:要创建的对象构造方法逻辑很复杂,即创建新的对象比较复杂时,使用原型模式会比直接new效率更高;
  2. 经常需要克隆:经常要创建一个和原对象属性相同的对象时,可以考虑原型模式。

三、扩展

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笔记+踩坑】设计模式——原型模式

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

Flutter GetX使用详解

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

【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 进行操作…...

作为开发者,可视化开发工具了解一下

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

Python:实现日历功能

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

2.9.C++项目:网络版五子棋对战之业务处理模块的设计

文章目录 一、意义二、功能三、管理&#xff08;一&#xff09;客户端请求&#xff08;二&#xff09;websocket 四、框架五、完整代码 一、意义 将所有的模块整合在一起&#xff0c;通过网络通信获取到客户端的请求&#xff0c;提供不同的业务处理。 服务器模块&#xff0c;是…...

springboot actuator 常用接口

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

知识点滴 - Email地址不区分大小写

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

同一个页面同一区域两个el-table在v-if下样式重叠问题

&#x1f349;正常情况下在radio切换时两个表格的样式应如下 &#x1f349;实际上用v-if显示时会出现以下问题&#xff08;本该属于时间段相同模块的表格却出现在时间段自定义的表格中&#xff09; &#x1f349;解决方案&#xff1a; &#x1f343;一、将v-if替换成v-show(…...

ExoPlayer架构详解与源码分析(6)——MediaPeriod

系列文章目录 ExoPlayer架构详解与源码分析&#xff08;1&#xff09;——前言 ExoPlayer架构详解与源码分析&#xff08;2&#xff09;——Player ExoPlayer架构详解与源码分析&#xff08;3&#xff09;——Timeline ExoPlayer架构详解与源码分析&#xff08;4&#xff09;—…...

【开题报告】基于Spring Boot的课程在线预约系统的设计与实现

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

React Hooks还有哪些常用的用法?

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

基于Java的学生学籍管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

Java内部类、匿名内部类、嵌套类详解

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

【兔子王赠书第3期】《案例学Python(进阶篇)》

文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展&#xff0c;Python将会得到越来越多开发者的喜爱和应用。因为Python语法简单&#xff0c;学习速度快&#xff0c;大家可以用更短的时间掌握这门语…...

【C刷题】day6

一、选择题 1、以下叙述中正确的是&#xff08; &#xff09; A: 只能在循环体内和switch语句体内使用break语句 B: 当break出现在循环体中的switch语句体内时&#xff0c;其作用是跳出该switch语句体&#xff0c;并中止循环体的执行 C: continue语句的作用是&#xff1a;在…...

MySQL精髓:如何使用ALL一次找到最大值

题目来自LeetCode 题目 表&#xff1a;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+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

如何确定Apache Kafka的大小和规模

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

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...