【深入理解设计模式】原型设计模式

在这里插入图片描述

原型设计模式

原型设计模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制已有对象来创建新对象,而无需直接依赖它们的具体类。这种模式通常用于需要频繁创建相似对象的场景,以避免昂贵的创建操作或初始化过程。

概述

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

结构

原型模式包含如下角色:

  • 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

实现

原型模式的克隆分为浅克隆和深克隆。

(经典面试题)什么是深克隆和浅克隆
答:在计算机内存中,每个对象都有一个地址,这个地址指向对象在内存中存储的位置。当我们使用变量引用一个对象时,实际上是将该对象的地址赋值给变量。因此,如果我们将一个对象复制到另一个变量中,实际上是将对象的地址复制到了这个变量中。
<-------------------------------------------------------------------------------------------------------------------->
浅拷贝是指将一个对象复制到另一个变量中,但是只是复制对象的地址,而不是对象本身。也就是说,原始对象的复制对象实际上是共享同一个内存地址的。因此,如果我们修改了复制对象中的属性或元素,原始对象中对应的属性或元素也会被修改。
<-------------------------------------------------------------------------------------------------------------------->
深拷贝是指将一个对象及其所有子对象都复制到另一个变量中,也就是说,它会创建一个全新的对象,并将原始对象中的所有属性或元素都复制到新的对象中。因此,如果我们修改复制对象中的属性或元素,原始对象中对应的属性或元素不会受到影响。

浅克隆:
  1. Java中的Object类中提供了 clone() 方法来实现浅克隆。 Cloneable 接口是抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类。代码如下:
/*** @author OldGj * @version v1.0* @apiNote 具体原型类-原型模式*/
public class Realizetype implements Cloneable {public Realizetype() {System.out.println("原型创建");}@Overridepublic Realizetype clone() throws CloneNotSupportedException {System.out.println("原型克隆");return (Realizetype) super.clone();}
}
/*** @author OldGj * @version v1.0* @apiNote 测试类 - 测试原型模式克隆*/
public class Client {public static void main(String[] args) throws CloneNotSupportedException {Realizetype realizetype = new Realizetype();Realizetype cloned = realizetype.clone();System.out.println(cloned==realizetype); // false}
}
  1. 使用BeanUtils实现浅拷贝
    BeanUtils 是 Apache Commons BeanUtils 库中的一个类,它提供了一组用于操作 Java Bean 对象的工具方法。Java Bean 是一种符合特定约定的 Java 类,通常包含私有字段、公共 getter 和 setter 方法。BeanUtils 库可以用于在不直接访问对象字段的情况下操作 Bean 对象的属性。
/*** @author OldGj 2024/02/23* @version v1.0* @apiNote 使用BeanUtils实现浅克隆 - 用户类*/
public class User {private String name;private String password;private Address address;--- 省略get/set方法
}
/*** @author OldGj 2024/02/23* @version v1.0* @apiNote 使用BeanUtils实现浅拷贝 - 地址类*/
public class Address {private String province;private String city;--- 省略get/set方法
}
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 测试类 - 测试原型模式克隆*/
public class Client {public static void main(String[] args) throws CloneNotSupportedException, InvocationTargetException, IllegalAccessException {User user = new User();user.setAddress(new Address("beijing","shanghai"));User user1 = new User();BeanUtils.copyProperties(user,user1);System.out.println(user == user1); // falseSystem.out.println(user.getAddress() == user1.getAddress()); // true : 浅拷贝}
}
深克隆:
  1. 实现Cloneable接口,重写clone();

在Objecta类中定义了一个clone方法,这个方法其实在不重写的情况下,其实也是浅拷贝的。

如果想要实现深拷贝,就需要重写clone方法,而想要重写clone方法,就必须实现Cloneable,否则会报Clone NotSupportedException异常。

/*** @author OldGj 2024/02/23* @version v1.0* @apiNote 重写clone方法实现深克隆- 地址类*/
public class Address {private String province;private String city;--- 省略get/set方法
}
/*** @author OldGj * @version v1.0* @apiNote 重写clone方法实现深克隆 - 用户类*/
public class User implements Cloneable{private String name;private String password;private Address address;@Overrideprotected Object clone() throws CloneNotSupportedException {User user = (User) super.clone();user.setAddress(new Address());return user;}--- 省略get/set方法
}
/*** @author OldGj * @version v1.0* @apiNote 测试类 - 测试原型模式克隆*/
public class Client {public static void main(String[] args) throws Exception {User user = new User();User clone = (User) user.clone();System.out.println(user == clone); // falseSystem.out.println(user.getAddress() == clone.getAddress()); // false 深克隆}
}

这种方式的逻辑很好理解,就是在重写的clone()方法中,将对象的所有引用类型的属性全部设置为一个新实例化的引用即可。

但是采用这种方式也有一个缺点就是,如果我们User类中引用类型的属性非常多,那么clone()方法需要写很长,而且后面如果有修改,比如在User类中新增了属性,那么这个地方也要修改,并且通过这种方式实现深克隆后,我们也无法再调用上述浅克隆的方式进行浅克隆了。

  1. 序列化实现深克隆:我们可以借助序列化来实现深拷贝。先把对象序列化成流,再从流中反序列化成对象,这样就一定是新的对象了。

序列化的方式有很多,比如我们可以使用各种JSON工具,把对象序列化成SON字符串,然后再从字符串中反序列化成对象。

使用fastjson工具包序列化实现深拷贝:

/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 奖状类*/
public class Citation implements Cloneable, Serializable {private Student student;public void show() {System.out.println(student.getName() + "获得了奖状");}public Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}@Overrideprotected Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}
}
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 学生类*/
public class Student implements Serializable {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

/*** @author OldGj * @version v1.0* @apiNote 客户端 - 测试深拷贝 - 使用fastjson工具包实现序列化*/
public class CitationTest {public static void main(String[] args) throws Exception {Citation citation = new Citation();citation.setStudent(new Student());String jsonString = JSON.toJSONString(citation);Citation newCitation = JSON.parseObject(jsonString, Citation.class);System.out.println(citation == newCitation); // false System.out.println(citation.getStudent() == newCitation.getStudent()); // false 深拷贝}
}

使用SerializationUtils工具类实现深拷贝

SerializationUtils 是 Apache Commons Lang 库中的一个工具类,用于实现 Java 对象的序列化和反序列化。它提供了一组静态方法,可以方便地将对象序列化为字节数组,或者将字节数组反序列化为对象。
引入Apache Commons Lang库依赖:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version> <!-- 或者是当前最新版本 -->
</dependency>
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 客户端 - 测试类 测试深拷贝*/
public class CitationTest2 {public static void main(String[] args) throws CloneNotSupportedException {Citation citation = new Citation();Student student = new Student();student.setName("张三");citation.setStudent(student);Citation cloned = SerializationUtils.clone(citation); //基于序列化实现深拷贝System.out.println(citation==cloned); // falseSystem.out.println(citation.getStudent()==cloned.getStudent()); // false 深克隆Student student1 = new Student();student1.setName("李四");cloned.setStudent(student1); // 修改克隆对象的引用类型属性,原对象的该属性不变System.out.println(citation.getStudent().getName());System.out.println(cloned.getStudent().getName());}
}

注意事项
序列化版本一致性:确保序列化和反序列化的类具有相同的 serialVersionUID,以避免反序列化时出现 InvalidClassException。

Serializable 接口:要序列化一个对象,该对象的类必须实现 Serializable 接口。

SerializationUtils 提供了一种简单而强大的方式来实现对象的序列化和反序列化,以及对象的深度复制,从而简化了 Java 应用程序中的对象操作。Citation类和Student类必须实现Serializable接口,否则会抛NotSerializableException异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/487746.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring篇----第三篇

系列文章目录 文章目录 系列文章目录前言一、使用 Spring 有哪些方式?二、什么是 Spring IOC 容器?三、什么是依赖注入?四、可以通过多少种方式完成依赖注入?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这…

Ansible 更换aliyun 镜像 并下载tree

目录 查看系统版本找到对应 的版本对当前镜像进行备份下载aliyuan更换成功安装扩展源更换源之后 的三个命令 这里安装一个aliyun 的镜像 本案例 仅供实验参考 生产环境中请谨慎使用 查看系统版本 先查看linux 的系统 版本 ansible slave -m shell -a uname -a找到对应 的版本…

【办公类-16-10-02】“2023下学期 6个中班 自主游戏观察记录(python 排班表系列)

背景需求&#xff1a; 已经制作了本学期的中4班自主游戏观察记录表 【办公类-16-10-01】“2023下学期 中4班 自主游戏观察记录&#xff08;python 排班表系列&#xff09;-CSDN博客文章浏览阅读398次&#xff0c;点赞10次&#xff0c;收藏3次。【办公类-16-10-01】“2023下学…

邮件发送/接收过程分析、常见邮箱sport/dport列举、检测规则开发思路分析

一、邮件发送和接收过程分析 &#xff08;转载自&#xff1a;邮件的发送和接收过程——STMP、POP、IMAP、MIME_当收件人接收电子邮件时自己的邮件服务器通过什么收文件-CSDN博客&#xff09; 电子邮件发送协议 是一种基于“ 推 ”的协议&#xff0c;主要包括 SMTP &#xff1…

javaweb day3 day4 day5

js 引入方式 写法 基础语法 写法 变量 写法 数据类型 运算符 与java相同 会判断类型是否相同 循环控制语句 和java相同 函数&#xff08;方法&#xff09; 写法 Array数组 写法 string字符串 写法 js自定义对象 写法 JSON 写法 BOM window 写法 location 写法 DOM 案例…

TensorFlow2.x 精选笔记(1)数据基本操作与线性代数

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning 一、数组与张量 虽然张量看起来是复杂的对象&#xff0c;但它们可以理解为向量和矩阵的集合。理解向量和矩阵对于理解张量至关重要。 向量是元素的一维列表&#xff0c;向量是一…

【这个词(Sequence-to-Sequence)在深度学习中怎么解释,有什么作用?】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;深度学习笔记 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Sequence-to-Sequence&#xff08;Seq2Seq&#xff09; Sequence-to-Sequence&#xff08;Seq2Seq…

【深度学习笔记】卷积神经网络——填充和步幅

填充和步幅 在前面的例子 fig_correlation中&#xff0c;输入的高度和宽度都为 3 3 3&#xff0c;卷积核的高度和宽度都为 2 2 2&#xff0c;生成的输出表征的维数为 2 2 2\times2 22。 正如我们在 sec_conv_layer中所概括的那样&#xff0c;假设输入形状为 n h n w n_h\tim…

linux前端部署

安装jdk 配置环境变量 刷新配置文件 source profile source /etc/profile tomcat 解压文件 进去文件启动tomcat 开放tomcat的端口号 访问 curl localhsot:8080 改配置文件 改IP,改数据库名字&#xff0c;密码&#xff0c; 安装数据库 将war包拖进去 访问http:…

伪装成简历的攻击样本,HR小伙伴注意啦

样本简介 样本伪装成个人求职简历进行钓鱼攻击&#xff0c;名称为简历.docm&#xff0c;如下所示&#xff1a; 打开简历样本之后&#xff0c;提示包含宏代码&#xff0c;如下所示&#xff1a; 查看简历内容&#xff0c;发现竟然还是伪造的一份安全研究人员的简历&#xff0c;而…

前端学习——vue学习

文章目录 1. < el-form> 属性 model、prop、rules2. v-bind 与 v-model3. v-if 与 v-show4. v-for 循环语句5. 计算属性 computed6. 监视属性 watch7. 下拉框 el-select、el-option8. 自定义事件9. async与await实现异步调用 1. < el-form> 属性 model、prop、rule…

如何通过沉浸式翻译插件来高效阅读英文技术网站、英文文献

对于英语非母语的同学来说&#xff0c;阅读英文文档、英文技术博客、社区时经常会需要查询字典&#xff0c;打断了常规的阅读&#xff0c;效率极低&#xff0c;这时可以借助一款AI翻译插件&#xff08;沉浸式翻译&#xff0c;支持GPT翻译、谷歌翻译、百度翻译、有道翻译等&…