原型模式--深拷贝和浅拷贝

news2024/9/26 1:27:56

定义

Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. (使用原型实例指定将要创建的对象类型,通过复制这个实例创建新的对象。)

从定义中我们我们可以发现,该模式的前提是首先需要有一个对象,然后基于已有的对象复制生成新的对象,新生成的对象状态和原对象是一样的,不一样的是内存地址。

实现

public interface ICloneable<T> extends Cloneable {
    public T copy();
}

public class Message implements ICloneable<Message>{

    int what;
    String descriotion;

    @Override
    public Message copy() {
        Message clone = null;
        try {
            clone = (Message) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }
}

上述只有两个类,一个是ICloneable接口,该接口继承自java.lang.Cloneable接口,Cloneable接口是一个标记接口,本身没有什么方法,但是调用某个对象的clone方法必须要实现该接口,这里我借助该接口并扩展了ICloneable接口,所有ICloneable接口的实现类都必须要具备复制自己的能力,你可以发现,当前的设计中没有要求实现者必须是深拷贝或者浅拷贝。此外这里的接口作为一个标记来处理的方式很精巧,在日常开发中我们也可以这样做,比如设计一个防混淆的接口,所有该接口的实现者都在proguard中配置不混淆。
第二个类是ICloneable实现者,其copy方法对外提供了复制自身的能力,Message类中的what变量属于基本数据类型,该变量会被直接复制后赋值给新对象,而description变量是对象类型,目前copy方法采用了默认处理(clone方法默认是浅拷贝),所以该变量仍然指向旧对象中decscription变量所指向的string对象。

扩展

在解释原型模式的定义时,我们说原型对象是基于已有对象进行复制来生成新的对象,那么新生成对象时会执行构造函数吗?
在Java中,创建一个堆上对象有五种方式:

  • 使用new关键字 → 调用了构造函数
  • 使用Class类的newInstance方法 → 调用了构造函数
  • 使用Constructor类的newInstance方法 → 调用了构造函数
  • 使用clone方法 → 没有调用构造函数
  • 使用反序列化 → 没有调用构造函数

clone的行为是很简单的。以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。

浅拷贝(Shallow Clone):当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没没复制。如我想将A复制一份出来,命名为为B,那么我在浅拷贝后,确实可以得到A和B。而且A和B的值也相等,但是,我将B的值稍作修改,A的值也会变动,这往往不是我们想要的。因为我们想拷贝一个副本出来,二者也能独立,这样才算拷贝。但是,浅拷贝后A和B却是指向同一片地址空间,也就是二者共用一个值,改一个,两个都变。

浅拷贝实现:

重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,需要拷贝的类需要将clone方法的作用域修改为public类型。

注:Object类的clone方法只会拷贝java中的8中基本类型以及他们的封装类型,另外还有String类型。对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

public class Student implements Cloneable {
    private String name;
    private int age;

    private Address addr;

    public Student(String name, int age, Address addr) {
        this.name = name;
        this.age = age;
        this.addr = addr;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Address getAddr() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", addr=" + addr +
                '}';
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Address implements Cloneable {
    private String addrName;

    public Address(String addrName) {
        this.addrName = addrName;
    }

    public String getAddrName() {
        return addrName;
    }

    public void setAddrName(String addrName) {
        this.addrName = addrName;
    }

    @Override
    public String toString() {
        return "Address{" +
                "addrName='" + addrName + '\'' +
                '}';
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student = new Student("zhangsan", 13, new Address("China"));

        Student clone = (Student) student.clone();

        System.out.println(student);
        System.out.println(clone);

        student.setName("lisi");
        student.getAddr().setAddrName("Canada");
        System.out.println(student);
        System.out.println(clone);

    }
}

深拷贝(Deep Clone):除了对象本身被复制外,对象所包含的所有成员变量也被复制,就是我们想要的那种拷贝,即有一个副本,与原者老死不想往来,互不影响。

把上面的Student中clone方法改造一下,即可以实现深拷贝:

@Override
public Object clone() throws CloneNotSupportedException {
    Student newStu = (Student) super.clone();
    newStu.addr = (Address) this.getAddr().clone();
    return newStu;
}

拷贝的时候,将对象中的引用数据也拷贝一份即可。

2、可以使用序列化 + 反序列化的方式实现深拷贝:

public Object deepCopy(Object object) {
  ByteArrayOutputStream bo = new ByteArrayOutputStream();
  ObjectOutputStream oo = new ObjectOutputStream(bo);
  oo.writeObject(object);
  
  ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
  ObjectInputStream oi = new ObjectInputStream(bi);
  
  return oi.readObject();
}

 

原型模式的优点:

  1. 简化对象的创建过程,通过复制一个已有对象实例可以提高新实例的创建效率
  2. 扩展性好
  3. 提供了简化的创建结构,原型模式中的产品的复制是通过封装在原型类中的克隆方法实现的,无需专门的工厂类来创建产品
  4. 可以通过深克隆的方式保存对象的状态,使用原型模式将对象复制一份并其状态保存起来,以便在需要的时候使用,可辅助实现撤销操作
  • 原型模式的缺点:
  1. 需要为每一个类准备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有类进行改造时,需要修改原代码,违背了开闭原则
  2. 在实现深克隆时需要写较复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类必须支持深克隆,实现起来较烦较烦....
  • 原型模式的适用环境:
  1. 创建新对象成本较大,新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量修改
  2. 系统要保存对象的状态,而对象的状态变化很小
  3. 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更方便

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年4月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

4月&#xff0c;以太坊上海升级与香港Web3动向成最大热点&#xff0c;上海升级的完成是转POS的重要里程碑&#xff0c;从市场而言&#xff0c;由于升级解锁质押ETH是否引发抛压备受关注&#xff0c;仅以交易表现来看&#xff0c;并未出现大范围的抛压与离场。另一方面&#xff…

算力提升+AIGC,是驱动元宇宙发展的核心引擎|数据猿直播干货分享

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 “元宇宙”是美国科幻小说家尼奥斯蒂文森1992年在《雪崩》中提出的概念&#xff0c;书中设定现实世界中的人在网络世界中都有一个分身&#xff0c;这个由分身组成的世界就是“元宇宙”。如今&#xff0c;随着虚拟现实技术的…

60+开箱即用的工具函数库xijs更新指南(v1.2.5)

xijs 是一款开箱即用的 js 业务工具库, 聚集于解决业务中遇到的常用函数逻辑问题, 帮助开发者更高效的开展业务开发. 接下来就和大家一起分享一下v1.2.5 版本的更新内容以及后续的更新方向. 贡献者列表: 1. 数据深拷贝cloneDeep 该模块主要由 20savage 贡献, 支持 symbol, map,…

BM58-字符串的排列

题目 输入一个长度为 n 字符串&#xff0c;打印出该字符串中字符的所有排列&#xff0c;你可以以任意顺序返回这个字符串数组。 例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。 数据范围&#xff1a;n < 10。 要求&#xff1a;空…

[架构之路-191]-《软考-系统分析师》-8-软件工程 - 解答什么是面向功能的结构化程序设计:算法+数据结构 = 程序

目录 1. 什么是结构化程序设计 2. 结构化程序设计的局限性 3.程序设计的三种基本结构 (1) 顺序结构 (2) 选择结构 (3) 循环结构 1. 什么是结构化程序设计 功能 》 Function 》 函数 》 算法 数据流Data Flow 》 数据结构Data Strucuture 程序 算法 数据结构 》 数…

36. Kubernetes 网络原理——CNI 网络插件

本章讲解知识点 Flannel 原理概述直接路由的原理和部署示例Calico 插件原理概述1. Flannel 原理概述 Flannel 是一个用于容器网络的开源解决方案,它使用了虚拟网络接口技术(如 VXLAN)和 etcd 存储来提供网络服务。它的原理概述如下: Flannel 协助 Kubernetes,给每一个 No…

界面交互篇:答题页的答题逻辑交互开发

微信小程序云开发实战-答题积分赛小程序 界面交互篇:答题页的答题逻辑交互开发 前面的那一篇文章,我们已经完成了使用云开发的聚合能力实现从题库中随机抽取题目功能。 在页面加载时,实现从题库中随机抽取题目功能。那么,拿到数据后要干什么?如何做? 动态数据绑定 实…

c++练习题

1、默认参数练习 创建默认参数函数 void stars(int cols ,int rows ) 该函数默认缺省值cols是10 rows是1。该函数完成功能是根据行和列数显示一个由星号组成的矩形。在main函数仲按照默认值调用该函数。按照cols是5调用该函数。按照列数和行数是7&#xff0c;3 调用该函数 #…

【MMdetection训练及使用脚本系列】MMdetection训练1——如何保存最优的checkpoint文件

MMdetection如何保存最优的checkpoint文件 以目标检测为例&#xff0c;进入到 configs/_base_/datasets/coco_detection.py将evaluation dict(interval1, metricbbox)改为evaluation dict(interval1, metricbbox, save_bestauto)即可。 但是不建议这样做&#xff0c;防止以…

软件设计师笔记--数据结构

文章目录 前言学习资料数据结构大 O 表示法时间复杂度线性结构和线性表线性表的顺序存储线性表的链式存储栈的顺序存储栈的链式存储队列的顺序存储与循环队列 串KMP 数组矩阵树二叉树二叉树的顺序存储结构二叉树的链式存储结构二叉树的遍历平衡二叉树二叉排序树最优二叉树(哈夫…

C/C++每日一练(20230507) 数列第n项值I/II、简化路径

目录 1. 求数列的第n项的值 ※ 2. 求数列的第n项的值 II ※ 3. 简化路径 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 求数列的第n项的值 已知数列…

摘要:PostgreSQL开发技术基础:过程与函数

原文地址 6.0 Language SQL与PL/pgSQL PL / PgSQL是基于SQL的特定于PostgreSQL的过程语言 。它有循环&#xff0c;variables&#xff0c;错误/exception处理等等。并不是所有的SQL都是有效的PL / PgSQL&#xff0c;正如你发现的那样&#xff0c;例如&#xff0c;你不能在没有…

Nature:时松海课题组揭示调控大脑新皮层神经元空间精细结构排布和环路组装的新机制

2022年12月7日&#xff0c;清华大学生命科学学院、清华-IDG/麦戈文脑科学研究院、生命科学联合中心、生物结构前沿研究中心时松海教授课题组在Nature杂志以长文的形式在线发表了题为“Patterned cPCDH expression regulates the fine organization of the neocortex”&#xff…

Python心经(2)

有关数字类型&#xff0c;字符串&#xff0c;函数 目录 有关数字类型&#xff0c;字符串&#xff0c;函数 数字 字符串 索引操作 切片操作 单个字符编码 运算符 还有一些常用的内置函数 Python输入函数 输出函数print()语法 python的函数也能给默认值 Python是个脚…

什么是事件驱动的微服务架构?

对于许多关键应用程序功能&#xff0c;包括流媒体和电子商务&#xff0c;单体架构已不再足够。随着实时事件数据和云服务使用的需求增加&#xff0c;许多现代应用程序&#xff0c;如Netflix和Lyft&#xff0c;已经转向了事件驱动的微服务方法。分离的微服务可以独立运行&#x…

体外诊断(IVD)高速发展

体外诊断&#xff08;IVD&#xff09;实际一直是临床的重要组成部分。体外诊断&#xff0c;是指通过对人体样本(血液、体液、组织等)进行检测而获取临床诊断信息&#xff0c;进而判断疾病或机体功能的产品和服务&#xff0c;其检测原理和方法涉及免疫学、微生物学、分子生物学等…

Cursor设置中文版 以及简单实用教程集成 GPT4 的代码神器 Cursor

官网&#xff1a;cursor.so 快捷键 ctrl K&#xff0c; 中英文提交需求&#xff0c;&#xff08;刚开始我整蹩脚的英语&#xff0c;后来发现支持中文&#xff09; 自动写代码 自动补充代码 自己修改代码 自动检查、完善代码 自己本地测试 然后再重复上面操作直到能用 最后让它…

ZooKeeper知识回顾(分布式协调框架,本质是分布式小文件存储系统,以分布式集群部署)

Apache ZooKeeperhttps://zookeeper.apache.org/官网为: 名字.apache.org 1.zookeep概念 一个团队里面&#xff0c;需要一个leader&#xff0c;leader是干嘛用的&#xff1f; &#xff08;分布式协调框架&#xff09; 1.管理什么的咱不说。 2.外面的人&#xff0c;想…

AI大神吴恩达与OpenAI官方合作推出的ChatGPT提示工程课,到底在讲什么?

ChatGPT提示工程课程&#xff0c;吴恩达&OpenAI 概述 本课程将着重介绍指令调优LM的开发最佳实践&#xff0c;以帮助开发人员利用LM技术构建聊天机器人等应用程序。 亮点 &#x1f4da; LM可用于快速构建软件应用程序&#xff0c;API可以使开发人员非常快速地构建。&#x…

怎么压缩照片到20k?

怎么压缩照片到20k&#xff1f;如今&#xff0c;手机摄影已经成为我们生活的一部分&#xff0c;我们经常使用手机拍照记录美好瞬间。但是&#xff0c;随着手机拍照像素的不断提高&#xff0c;照片的文件大小也越来越大&#xff0c;这使得手机的存储空间不够用&#xff0c;甚至在…