设计模式之原型模式(深拷贝浅拷贝)

news2024/11/15 7:56:19

目录

1、什么是原型模式

2、前置知识(深拷贝&浅拷贝)

2.1 浅拷贝

2.2 深拷贝

3、代码实现

3.1 通过Object中的clone方法实现浅拷贝

3.2 通过对象流来实现深拷贝

4、原型模式总结

4.1 优缺点

4.2 使用场景

4.3 对比直接new对象有何不同

1、什么是原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

2、前置知识(深拷贝&浅拷贝)

2.1 浅拷贝

创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原
有属性所指向的对象的内存地址。

2.2 深拷贝

创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

3、代码实现

3.1 通过Object中的clone方法实现浅拷贝

学生类:

public class Student implements Cloneable{ //学生类,要使用clone的话要实现Cloneable接口,否则会报错
    private String name; //学生姓名
    private int age; //学生年龄
    public Student(String name,int age){ //构造函数
        this.name=name;
        this.age=age;
    }
    public void get(){ //输出学生信息
        System.out.println("名字是:"+name+"------年龄是:"+age);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException { //重写clone方法
        return super.clone();
    }

    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 class Seat implements Cloneable{//座位类,每个座位和学生一一对应
    private int num; //座位号
    private Student student; //这个座位属于的学生
    public Seat(int num,Student student){
        this.num=num;
        this.student=student;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    @Override
    public Seat clone() {
        try {
            return (Seat) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

测试类:

public class Test2 {
    public static void main(String[] args) {
        Student s1 = new Student("TowsJel", 19);
        Seat seat1 = new Seat(1, s1);
        Seat clone = seat1.clone();
        System.out.println("复制后的对象和原对象是否是同一个?------"+(clone==seat1));
        System.out.println("------验证是浅拷贝------");
        System.out.println("seat1中的学生对象和clone中的学生对象是否是同一个?------"+(seat1.getStudent()==clone.getStudent()));
    }
}

运行结果如下:

 大家会觉得,诶,这又会怎么样呢,浅拷贝不就是里面的引用对象没有复制一份而已吗,会影响什么吗?大家来想想,如果此时,我们要修改clone中学生的名字,是不是seat1中的名字也会发生修改了?换句话说,clone这个座位的名字和seat1这个座位的名字是不是就是同一个人了?大家想想,这合理嘛!显然不合理!

那么如果想通过深拷贝的方式实现原型模式应该怎么办呢?那就需要通过“对象流”的方式来实现了。

3.2 通过对象流来实现深拷贝

基于上述的学生类和座位类,我们写得以下测试类:

public class Test3 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student stu = new Student("张三", 19);
        Seat s1 = new Seat(1,stu);
        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(new
                FileOutputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        //将s1对象写出到文件中
        oos.writeObject(s1);
        oos.close();
        //创建对象出入流对象
        ObjectInputStream ois = new ObjectInputStream(new
                FileInputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        //读取对象
        Seat s2 = (Seat) ois.readObject();
        //判断stu对象和stu1对象是否是同一个对象
        System.out.println("stu和stu1是同一个对象?" + (s1.getStudent()==s2.getStudent()));
    }
}

显然,最后运行结果肯定不是同一个对象。因此我们通过对象流的方式实现了深拷贝。

注意:使用对象流序列化对象的时候的时候要让该类实现Serializable接口,否则会有异常:NotSerializableException。

4、原型模式总结

4.1 优缺点

优点:1、性能提高。 2、逃避构造函数的约束。

缺点:1、必须实现Cloneable接口。

4.2 使用场景

1、资源优化场景。

2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

3、性能和安全要求的场景。

4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

5、一个对象多个修改者的场景。

6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。

4.3 对比直接new对象有何不同

与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写clone方法;深拷贝是通过实现 Serializable 读取二进制流。

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

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

相关文章

如何使用递归函数实现Excel列号转换列标

在Excel中,列标与列号转换是VBA开发过程中经常用到的功能,下面这篇博客为大家解释了多种方法。 【Excel列标与列号转换】 那么这篇博文的核心是“递归过程”,实现这个功能并不是必须使用递归过程,但是这也不失为一种实现方法&am…

【Android入门到项目实战-- 8.2】—— 使用HTTP协议访问网络

目录 一、使用HttpURLConnection 1、使用Android的HttpURLConnection步骤 1)获取HttpURLConnection实例 2)设置HTTP请求使用的方法 3)定制HTTP请求,如连接超时、读取超时的毫秒数 4)调用getInputStream()方法获取返回的输入流 5)关闭HTTP连接 2、…

NXP - LPC1769与LPC1768的区别

文章目录 NXP - LPC1769与LPC1768的区别概述笔记General description验证结论END NXP - LPC1769与LPC1768的区别 概述 openpnp设备用到了冰沙主板. 冰沙主板的主控MCU用到了LPC1769, 想着研究一下. 订了OM13085UL, 遥遥无期… 买了LPC MCU的书, 里面提到了书的作者的网店, 居…

python+vue精品课程建设制作django服务网站系统

功能介绍通篇文章的撰写基础是实际的应用需要,然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程,以视频建设制作服务的实际应用需要出发,架构系统来改善现视频建设制作服务工作流程繁琐等问题。不仅如此以操作者的…

kotlin在鸿蒙开发中的实践

先说一说kotlin 我们知道: kotlin目前是安卓首选的编程语言。 安卓逐渐抛弃java,拥抱kotlin这是大的趋势。 kotlin的最大优点就是与java的互操作性。 kotlin编译的产物和java一样是bytecode(不抬杠,本文只说面向jvm的kotlin)。 kotlin是一…

Cadence基础操作:Schematic编辑

本文转载自B站up主:_WithB,原文链接如下:https://www.bilibili.com/read/cv20414466 鼠标 左键单击 –> 选中或确定操作 按住左键 –> 选中区域内所有组件 左键双击,可以选择以特定操作模式和窗口类型进入对应组件的下一层一般我是ed…

Winform从入门到精通(36)—ColorDialog(史上最全)更新中

前言 当我们需要设置某个控件的颜色时,并且需要弹出一个可以选择颜色的对话框时,这时候就需要使用ColorDialog 一、属性 1、AllowFullOpen 该属性用于启用或者禁用“自定义颜色按钮”,该属性为true时,可以自定义颜色 2、AnyColor 实际测试该属性没什么作用 3、Colo…

请求与相应

从容器到Servlet 前面我们介绍了JSP的内置对象和Servlet的相关知识, 以及如何部署和开发一个Servlet。但是, 并没有详细介绍如何将Servlet与JSP结合起来使用。Web容器是JSP唯一可以识别的HTTP服务器, 所以必须了解Web容器如何生成请求和响应…

来上海一个月的记录、思考和感悟

作者 | gongyouliu 编辑 | gongyouliu 从4月3号早上来上海,到今天差不多整整一个月了,也是自己正式从杭州离职创业(我更愿意称之为自由职业者,毕竟我没有招聘全职员工,有两个朋友业余时间在帮我)的第一个月…

SAP UI5 之Bootstrap(引导)笔记二

文章目录 Setting up Visual Studio Code for UI5 development1.0 官网 Walkthrough学习-Bootstrap 引导加载1.0.1 在 index.html中新增script标签1.0.2 在webapp 下面新增index.js文件1.0.3启动UI5的服务 Setting up Visual Studio Code for UI5 development 学习链接 Setti…

如何正确部署Redisearch和Rejson(附*.so文件免费下载)

1 缘起 项目需要。 最近的一个项目需要做文本搜索,技术选型:Redis的两个组件Redisearch和ReJSON。 Redisearch和ReJSON是Redis的两个组件: RediSearch为Redis提供查询、二次索引和全文搜索。使用RediSearch,首先要在Redis数据上声明索引。然后使用RediSearch查询语言来查…

【电子通识】颜色的困惑:什么是国际通用Panone(潘通)

Pantone 是世界知名的色彩权威机构,也是色彩系统的供应商,为许多行业提供专业色彩选择。在 Pantone 之前,每个印刷公司都有自己的色彩指南。比如都是“黄色”,但由于印刷方式有所不同(具体取决于每个油墨公司如何解释该…

学习之-Mysql Sql 优化之 Explain

在开发中,往往遇到一些慢查询语句, 我们需要对慢查询进行优化。Explain工具就是用来分析某个慢查询执行情况的工具。通过在select 语句前加上explain 关键字,然后执行就会得到某个sql 执行计划信息,通过分析执行计划,我…

vue相关知识导学

学习资料 Vue 相关源码地址: vue2.0 GitHub - vuejs/vue: This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/coreVue3.0 GitHub - vuejs/core: 🖖 Vue.js is a progressive, incrementally-adoptable JavaScri…

【从0到1了解Libarchive】Libarchive的用途意义以及成功入门Libarchive

目录 0 如果你还不知道Libarchive是什么请一定要先看一下 1 简介 1.1 为什么实现Libarchive 1.2 到底都有谁在用呢? 1.3 Libarchive都有哪些功能 1.4 我们可以通过这些获取更多信息 1.5 如何贡献 2 Libarchive归档与压缩 3 Libarchive编译 4 Libarchive简…

maven gpg报错:no default secret key: No secret key signing failed: No secret key

一、问题描述 我这边情况是这样的,原本在A电脑上,通过maven打包安装都是好的,最近新弄了台电脑,然后把A电脑的gpg秘钥通过Kleopatra的方式直接导出来 然后在B电脑上通过Kleopatra导入,整了很久,在IDEA中执…

FreeRTOS 事件标志组

文章目录 一、事件标志组简介二、创建事件标志组1. 函数 xEventGroupCreate()2. 函数 xEventGroupCreateStatic() 三、设置事件位1. 函数 xEventGroupClearBits()2. 函数 xEventGroupClearBitsFromISR()3. 函数 xEventGroupSetBits()4. 函数 xEventGroupSetBitsFromISR() 四、获…

Linux pthread线程操作 和 线程同步与互斥操作

在Linux系统中玩线程,使用pthread,这篇博客记录如何创建线程和使用线程和线程的同步与互斥。 还有一份nginx线程池的代码供大家阅读学习! 目录 一、简介 什么是线程 线程的优点、缺点 线程的应用场合 二、线程的使用 1. 创建线程 - p…

如何评价聚类结果的好坏?

聚类有效性的评价可分为内部指标和外部指标,内部指标是一种无监督的评价方法,它对聚类结果的评价不需要借助样本集的真实标签,仅利用样本集自身 结构信息对聚类结果进行评价;而外部指标是一种有监督的评价方法,它通过对…

简单毛概刷题网页制作 3.0(拖欠近一年版)

原因是大概一年之前学校的毛概期末刷题网站突然崩了,但是一直没有修复。当时眼看着复习时间逐渐被压缩,自己啥也做不了,遂自学前端完成毛概刷题网页一枚。 最早的毛概刷题网站仅仅是 1.0 版本(传送门),功能…