Java设计模式之创建型-原型模式(UML类图+案例分析)

news2024/11/14 16:30:38

一、基础概念

通过复制已有对象作为原型,通过复制该原型来返回一个新对象,而不是新建对象,说白了就是不断复制相同的对象罢了。

二、UML类图

三、角色分析

角色描述
抽象原型类规定了具体的原型对象必须实现的clone()方法
具体原型类实现抽象原型类的clone()方法,它是可以为复制的对象
访问类使用具体原型类中的clone()方法来复制新的对象

四、案例分析 

1、通用实现(浅克隆)

定义一个学生类,实现Cloneable接口并重写clone方法。

super.clone()是基于对象在内存中的二进制位面值进行复制的一种浅拷贝实现。它的优点是效率高,不需要进行逐字段复制,因此不会调用对象的构造函数,也就是不需要经历初始化的过程。

    @Override
    protected Student clone(){
        Student student = null;
        try {
            student = (Student) super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return student;
    }

其内部属性有name和Teacher类,实现有参构造,get和set方法,重写toString()方法。 

public class Student implements Cloneable{

    private String name;

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

    private Teacher teacher;

    public Student(String name, Teacher teacher) {
        this.name = name;
        this.teacher = teacher;
    }

    public String getName() {
        return name;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    protected Student clone(){
        Student student = null;
        try {
            student = (Student) super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return student;
    }
}

写一个主方法测试:

    public static void main(String[] args) {
        Teacher teacher = new Teacher("赵老师");
        Student student = new Student("李四",teacher);
        Student clone = student.clone();
        clone.getTeacher().setName("老王老师");
        System.out.println(student);
        System.out.println(clone);
    }

运行结果如下:

运行完毕以后会发现一个问题,就是我克隆出来的学生换了新的老王老师以后,怎么原来学生对象的老师也变成了老王老师,这明显不对呀!

从运行的结果上分析,应该是teacher共用同一个内存地址,意味着复制的不是值,而是引用的地址,这正是浅拷贝的特征:

1、对基本数据类型进行值复制

2、对引用类型仅复制引用,没有复制引用的对象

解决办法是在clone时,需要深拷贝teacher对象,断开student和clone的teacher对象引用关系,使两者独立。

2、深克隆

Student类需要实现Serializable接口,并自定义了deepClone方法实现深克隆,每一行代码解释如下:

1、创建字节数组输出流,用于存放序列化后的二进制数据。

ByteArrayOutputStream bos = new ByteArrayOutputStream();

2、 基于字节数组输出流创建对象输出流,用于序列化对象。

ObjectOutputStream oos = new ObjectOutputStream(bos);

3、 将当前对象写入对象输出流进行序列化,序列化后的二进制数据存入字节数组输出流。

oos.writeObject(this);

4、获取字节数组输出流中的数据(序列化后的二进制数据),封装为字节数组输入流。

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

5、基于字节数组输入流创建对象输入流,用于反序列化对象。

ObjectInputStream ois = new ObjectInputStream(bis);

6、从对象输入流中读取流数据并反序列化生成对象,返回反序列化得到的学生对象副本。

return (Student) ois.readObject();

完整的关键代码如下: 

public class Student implements Cloneable, Serializable {
    
          
    //构造、get和set方法、toString()方法省略

    public Student deepClone(){
        try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (Student) ois.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

}

总结起来,这段代码使用了序列化和反序列化来实现对象的深克隆,核心思路是将对象写入流,然后从流里再读出来克隆对象。 

同时Teacher类也需要实现Serializable序列化接口,关键代码如下:

public class Teacher implements Serializable {}

但不需要实现深克隆,原因如下:

在通过序列化实现Student深克隆时,会自动将Student对象所引用的Teacher对象全部序列化,并在反序列化时重新创建出一个新的Teacher对象。

Teacher对象已经在这个序列化/反序列化的过程中被自动深克隆了,不需要再单独实现深克隆方法。

主方法测试:

    public static void main(String[] args) {
        Teacher teacher = new Teacher("赵老师");
        Student student = new Student("李四",teacher);
        Student clone = student.deepClone();
        clone.getTeacher().setName("老王老师");
        System.out.println(student);
        System.out.println(clone);
    }

运行结果如下: 

五、总结

优点:

避免重复创建成本高的对象。

客户端可以直接获得对象副本,不需要知道如何创建。

可以动态添加或者修改复制逻辑。

缺点:

需要为每一个类配置一个克隆方法。

复制对象的成本也存在,特别是深拷贝。

需要注意隔离对象状态,避免相互影响。

应用场景:

对象的创建成本比较大,可以通过复制原型对象避免重复创建。

需要重复创建相似对象时可以考虑原型模式。

需要避免使用子类式继承改变对象结构时。

原型模式通过对象复制获取实例,避免重复创建开销大的对象,是一种快速获取对象副本的模式,但需要注意副本状态的一致性管理。

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

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

相关文章

倒计时1天!LeaTech全球CTO领导力峰会TVP四周年庆典即将开幕

引言 3 月 4 日,上海扬子江丽笙精选酒店,LeaTech 全球 CTO 领导力峰会暨腾讯云 TVP 四周年、CTO 训练营校友联合庆典即将开幕。本次 LeaTech 全球 CTO 领导力峰会以“寻光之旅”为主题,腾讯云 TVP 携手 51CTO,联合邀请业内资深领袖…

C# winform界面显示3D点云图像(halcon+VTK)

前一段时间研究了下halcon里的3d算法,想着把3d图像显示在C#编写的软件界面上,试了下halcon的控件,没成功。后来学习了一点VTK的知识,实现了3d图像的显示,可旋转,平移,缩放观察,当然也…

为什么国内做不出好的3A游戏?

个人觉得原因如下: 主要原因: 市场需求和消费观念:国内游戏市场对游戏类型和风格有着自身的特点和需求。一些热门游戏类型,如多人在线游戏、手机游戏等,相对于传统的3A游戏更受国内玩家欢迎。这可能导致国内游戏公司…

QInputDialog 不显示ok或cancel按钮bug

今天遇到一个奇怪问题,就是调用 QInputDialog::getText去获取输入文本,但是无法显示系统ok和cancel按钮,我记得之前是可以的,于是我回退上一个版本是正常,于是对比两个版本代码,发现,自己重写 Q…

【人工智能】贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

文章目录 频率学派 vs. 贝叶斯学派贝叶斯学派Probability(概率):独立性/条件独立性:Probability Theory(概率论):Graphical models (概率图模型)什么是图模型(Graphical Models&…

stm32(定时器和PWM知识点)

一、定时器介绍 软件定时 缺点:不精确、占用CPU资源 void Delay500ms() //11.0592MHz { unsigned char i, j, k; _nop_(); i 4; j 129; k 119; do { do { while (--k); } while (--j); } while (--i); }定时器工作原理: 使用精准的时基&#xff0c…

面试靠微服务扭转局面,知乎夸爆的微服务学习笔记到底有多牛?

我们现在的服务-微服务 我感觉微服务不是一个架构,而是像一个生态,应用与应用之间互相独立,却又彼此依赖。通过 DDD 的模型来设计一个地图,把合适的代码放到合适的地方去。实现微服务涉及的工具太多,以下我采用spring…

Python -- 多任务、进程、线程

文章目录 多任务的介绍多任务的执行方式 进程进程的概念进程的作用进程的使用多进程获取进程编号 进程执行带有参数的任务介绍args参数和kwargs的使用 进程注意点进程之间不共享全局变量主进程会等待所有子进程执行结束再结束 线程介绍概念 线程的作用多线程的使用线程执行有参…

在OpenCV中进行图像预处理

今天,我们进一步深入,并处理在图像处理中常用的形态学操作。形态学操作用于提取区域、边缘、形状等。 什么是形态学操作? 形态学操作是在二值图像上进行的。二值图像可能包含许多不完美之处。特别是由一些简单的阈值操作产生的二值图像&#…

2023-07-10 cmake管理的项目中使用vcpkg管理第三方库

一、安装 从Github上克隆Vcpkg仓库然后执行安装命令即可: git clone https://github.com/microsoft/vcpkg .\vcpkg\bootstrap-vcpkg.bat 安装自己需要的第三方库 .\vcpkg\vcpkg install [packages to install] 更多教学可参考: https://learn.microsoft…

kubenetes手动安装V1.22.4

kubenetes手动安装V1.22.4 1、主节点和工作节点需要的组件 提示:为了方便测试请关闭selinux、关闭防火墙、swap. SELinux防火墙的设置: [rootlocalhost]#getenforce Disabled # 修改/etc/selinux/config文件 vim /etc/selinux/config 将SELINUXenfor…

【python】Excel文件的读取操作

测试用例.xlsx文件内容 利用xlrd模块读取文件 import xlrdfilename 测试用例.xlsx # 读取Excel表文件 wb xlrd.open_workbook(filename) # 读取Excel表中的第一个工作表 sheet wb.sheet_by_index(0) # 读取有内容的表格行数 rows sheet.nrows # 读取有内容的表格列数 cols…

嵌入式系统之ADC采样

嵌入式系统往往会有模拟信号的采集,比如模拟传感器温度、压力、流量、速度、光强等模拟量,经过放大整形滤波电路后送给ADC芯片,将电信号转转变成离散的数字量这个过程称之为AD采样,AD采样应用广泛,普遍遵循采样率3倍于…

ES 知识体系

https://www.easyice.cn/archives/367

【力扣算法03】之正则表达式匹配- python

文章目录 问题描述示例 1示例2示例3提示 思路分析代码分析完整代码运行效果及示例代码示例代码1运行结果示例代码2运行结果示例代码3运行结果 完结 问题描述 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘’ 的正则表达式匹配。 ‘.’ 匹配任意…

企业省时又省力:人工智能电话客服机器人的广泛应用

人工智能技术的崛起正深刻地改变着各个行业,其中之一便是客服领域。过去,人们常常在电话中与生硬的自动语音应答机打交道,这种体验常常令人沮丧。然而,随着人工智能电话客服机器人的广泛应用,企业不仅能够省时又省力&a…

MYSQL数据库系统期末试题及参考答案(2)

期末试题 : 一,创建数据库Game 二,数据表操作 1、创建表格players,记录游戏玩家信息: player_id:玩家ID,主键 player_name:玩家姓名,不能为空 age:年龄,必须…

Python数据分析常见Matplotlib SeaBorn图表

Matplotlib绘图 Matplotlib基本概念 Matplotlib:基于对象的思维构建的视觉符号。 每一个Axes(坐标轴)对象包含一个或者多个Axis(轴)对象,比如X轴、Y轴。 一个Figure(画像)是由一堆坐标轴对象组成的。 换…

2023中国数交会|美创科技获数字和软件服务行业两项大奖!

7月6日-9日,为期四天的2023中国国际数字和软件服务交易会(简称:数交会)圆满落幕。 作为国务院批准举办的国家级展会,本届数交会由商务部、科技部、中国贸促会和辽宁省政府主办,以“数字创新、融合发展”为主…

操作系统实战45讲|03.黑盒之中有什么、04.震撼的Linux全景图

03.黑盒之中有什么 黑盒之中有什么 从抽象的角度来看,内核就是计算机资源的管理者,管理资源是为了让应用使用资源。 计算机中的资源分为两类:硬件资源、软件资源; 硬件资源有以下这些: 总线,负责连接各种…