java设计模式之原型模式(prototype Pattern)

news2024/11/23 21:27:59

原型模式主要在于对象的克隆,所以也叫克隆模式

其实就是利用java中的Object对象中的clone方法实现一个对象的克隆。此方法需要注意的是,一个对象想要实现克隆,就必须实现一个标志性接口Cloneable

现在先来说一下浅克隆

这玩意也叫表皮克隆,简单点就是说里面的引用的引用对象没有进行实质性的克隆,而是指向一片共同空间

话不多说,直接上代码

ConcretePrototype.java

package pxx;



import java.util.List;

public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

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

 下面去看客户端Client.java

package pxx;

import java.util.ArrayList;
import java.util.List;

public class Client {


    public static void main(String[] args) {
        //创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("Tom");
        List<String> hobbies = new ArrayList<String>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);

        //拷贝原型对象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.setAge(520);
        cloneType.getHobbies().add("技术控");


        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneType);
        System.out.println(prototype == cloneType);//数据一样,地址一样


        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());//地址一样

    }

}

直接上一下打印结果

 

下面在来上一下浅拷贝的具体分析

 上面就很明显看到复制过去一个对象之后,就相当于在堆上面开了一个新空间,你做出相应的改值之后,这个只是对于普通类型来说,是对原对象互不影响的

所以上面prototype == cloneType这个进行判断的时候,会出现false,因为这两者的内存地址本来就不一样。这也就是为什么prototype.getHobbies() == cloneType.getHobbies() 的时候为什么返回true的原因,因为他们指向的内存地址都一样。

下面我们进入深克隆,解决prototype.getHobbies() == cloneType.getHobbies()内存地址不一样的问题

话不多说,直接上代码

ConcretePrototype.java

package pxx;



import java.util.ArrayList;
import java.util.List;

public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    public ConcretePrototype clone() {
        try {
            //无非就是把hobbies也进行一个克隆
            //ArrayList集合对象本身就是实现了Cloneable接口的
            //所以ArrayList可以直接实现克隆方法
            //但是List接口是没有去实现这个Cloneable接口的
            ConcretePrototype result = (ConcretePrototype)super.clone();
            //把这个对象内部的集合对象也做一个克隆
            result.hobbies = (List)((ArrayList)result.getHobbies()).clone();
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

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

客户端代码还是不改变,看一下运行结果

 

 

直接一张图分析一下它的克隆原理

 运行结果分析:

 

 

上面两个hobbies都指向了不同对象,自然比较内存地址是不相同的

但是你想想,现在也会存在这样一个问题,就是如果一个类里面有很多引用对象,如果要进行深拷贝,我们就要不断的去进行克隆,比如如下

ConcretePrototype.java

package pxx;
import java.util.ArrayList;
import java.util.List;

public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    //下面两个对象都是可变对象
    private List<String> hobbies;
    private BasePerson basePerson;

    @Override
    public ConcretePrototype clone() {
        try {
            //无非就是把hobbies也进行一个克隆
            //ArrayList集合对象本身就是实现了Cloneable接口的
            //所以ArrayList可以直接实现克隆方法
            //但是List接口是没有去实现这个Cloneable接口的
            ConcretePrototype result = (ConcretePrototype)super.clone();
            //把这个对象内部的集合对象也做一个克隆
            result.hobbies = (List)((ArrayList)result.getHobbies()).clone();
            //因为现在还有一个对象
            //注意BasePerson这个对象重写一下clone()方法
            result.basePerson = (BasePerson)(result.getBasePerson()).clone();
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public BasePerson getBasePerson() {
        return basePerson;
    }

    public void setBasePerson(BasePerson basePerson) {
        this.basePerson = basePerson;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                ", basePerson=" + basePerson +
                '}';
    }
}

//想要被克隆,必须去实现Cloneable接口
class BasePerson implements  Cloneable{
    private int age;
    private String name;

    public BasePerson(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

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

Client.java

package pxx;

import java.util.ArrayList;
import java.util.List;

public class Client {
    public static void main(String[] args) {
        //创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("Tom");
        List<String> hobbies = new ArrayList<String>();

        BasePerson basePerson = new BasePerson(20,"周杰伦");
        prototype.setBasePerson(basePerson);

        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);

        //拷贝原型对象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.setAge(520);
        cloneType.getHobbies().add("技术控");

        //这里是深拷贝,那么就是说,我们通过拷贝对象去修改BasePerson里面的值
        //不会影响原对象
        cloneType.getBasePerson().setAge(520);


        System.out.println("原型对象:" + prototype);//这里没有技术控,因为hobbies对象是两个不同对象
        System.out.println("克隆对象:" + cloneType);//这里的age与prototype也不一样,是520
        System.out.println(prototype == cloneType);//false


        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());//false
        //这里面表示的内存地址也不一样
        System.out.println(prototype.getBasePerson() == cloneType.getBasePerson());//false

    }

}

运行结果:

上面如果引用对象太多,克隆操作相对来说,还是比较复杂的。下面我们可以采用序列化方法来实现深度克隆,也就是我们直接把这个对象给序列化到内存里面,然后在解析出来,这样就会整体在内存里面开一个新对象与原对象无任何关系。而且,此时这个对象内部的引用对象不需要显示实现Cloneable接口,重写clone方法,与此无关

ConcretePrototype.java

package pxx;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ConcretePrototype implements Cloneable,Serializable{

    private int age;
    private String name;
    //下面两个对象都是可变对象
    private List<String> hobbies;
    private BasePerson basePerson;

    @Override
    public ConcretePrototype clone() {
        //后面要用到这个流里面的数据,所以,必须拿出来
        //另外这个流不需要关闭
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)
             ){
            //你必须明白你要序列化的是哪一个对象
            objectOutputStream.writeObject(this);
        } catch (IOException e) {
            e.printStackTrace();
        }

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        try (ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)
        ){
            //开始把对象写到内存里面去
            ConcretePrototype concretePrototype = (ConcretePrototype) objectInputStream.readObject();
            return concretePrototype;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public BasePerson getBasePerson() {
        return basePerson;
    }

    public void setBasePerson(BasePerson basePerson) {
        this.basePerson = basePerson;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                ", basePerson=" + basePerson +
                '}';
    }
}

//想要被克隆,必须去实现Cloneable接口
//序列化一个类必须实现Serializable
class BasePerson implements Serializable{
    private int age;
    private String name;

    public BasePerson(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

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

 client.java这个还是没变

package pxx;

import java.util.ArrayList;
import java.util.List;

public class Client {
    public static void main(String[] args) {
        //创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("Tom");
        List<String> hobbies = new ArrayList<String>();

        BasePerson basePerson = new BasePerson(20,"周杰伦");
        prototype.setBasePerson(basePerson);

        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);

        //拷贝原型对象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.setAge(520);
        cloneType.getHobbies().add("技术控");

        //这里是深拷贝,那么就是说,我们通过拷贝对象去修改BasePerson里面的值
        //不会影响原对象
        cloneType.getBasePerson().setAge(520);


        System.out.println("原型对象:" + prototype);//这里没有技术控,因为hobbies对象是两个不同对象
        System.out.println("克隆对象:" + cloneType);//这里的age与prototype也不一样,是520
        System.out.println(prototype == cloneType);//false


        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());//false
        //这里面表示的内存地址也不一样
        System.out.println(prototype.getBasePerson() == cloneType.getBasePerson());//false

    }

}

运行结果

简单说一下原理

 

 

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

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

相关文章

【前端开发学习】1.前端引入和HTML标签

文章目录1.快速开发网站2. 浏览器能识别的标签2.1 编码&#xff08;head&#xff09;2.2 Title&#xff08;head&#xff09;2.3 标题&#xff08;body&#xff09;2.4 div 和 span&#xff08;body&#xff09;2.5 超链接&#xff08;body&#xff09;2.6 图片&#xff08;bod…

基于微信小程序的居民疫情服务系统springboot框架-计算机毕业设计

项目介绍 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;居民疫情服务系统小程序被用户普遍使用&#xff0c;为…

一个基于.Net开发齐全的加密库

网络安全&#xff0c;对于我们项目日常开发&#xff0c;是非常重要的&#xff0c;比如密码加密存储、密码加密传输、SSL证书、端对端加密等等。 这些加密算法&#xff0c;每个知识点都包含了很多内容&#xff0c;自己实现都需要花费很多时间&#xff0c;所以今天就给大家推荐一…

Qt 模型视图编程之增删行列

背景 Qt 模型视图编程中模型定义了标准接口对数据进行访问&#xff0c;可根据需求继承对应的抽象模型类来实现自定义的数据模型。一个基本的数据模型至少要实现以下虚函数&#xff1a; ①&#xff0e;rowCount&#xff1a;行数&#xff0c;返回要显示多少行&#xff1b; ②&…

net基于asp.net的社区团购网站-计算机毕业设计

项目介绍 社区团购系统依托社区团购系统和社区门店,是现在的一个重大市场和发展方向,通过研究企业在社区团购系统环境下的营销模式创新,对于普通的零售业和传统社区团购系统的转型发展具有重要的理论意义。随着互联网行业的发展,人们的生活方式发生着重大变化,人们越来越倾向于…

[附源码]Python计算机毕业设计Django酒店客房管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

[附源码]Nodejs计算机毕业设计基于大学生兼职系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

前端基础(七)_表单的基本组成与使用

表单 表单&#xff1a;用于采集不同类型的用户输入数据&#xff0c;发送给服务器&#xff0c;实现用户和服务器之间的数据交互&#xff1b; 表单标签 form 用于声明表单&#xff0c;定义数据的采集范围 注意&#xff1a; 1、一个页面中可以有多个表单标签&#xff0c;但是标…

STM32F4 | 串行通信基本原理 | 串口通信实验

文章目录一、串行通信基本原理1.串行通信接口背景知识2.异步串口通信UART知识3.STM32串口数据格式和通信过程4.STM32串口框图5.波特率计算方法二、STM32F429 串口简介三、硬件设计四、软件设计五、实验现象六、STM32CubeMX 配置串口本章介绍如何使用 STM32F429 的串口来发送和…

手把手教你Spring Cloud Alibaba教程:nacos安装

我们在学习springCloud的时候用的注册中心是Eureka: springBoot集成springCloud&#xff08;一&#xff09;注册中心 但是由于houlai Eureka2.0后续不维护&#xff0c;国内就需要一个可靠的注册中心。但是换了一套后&#xff0c;有哪些不同呢 SpringCloud和SpringCloudAliba…

fl21怎么换主题flstudio皮肤怎么换?

FLstudio21如何更改皮肤主题&#xff1f;不光是背景&#xff0c;还有按键什么的&#xff1f; 请参考下面的步骤进行设置fl视图设置。 第1步&#xff0c;打开visualstudiofl视图设置。 第2步fl视图设置&#xff0c;打开“工具”--“选项” 第3步&#xff0c;在“环境”--“常…

基于SpringBoot+MySql的分页功能实现

分页功能是为了缓解数据库的压力而实现的功能,实际上是将数据库中的数据分段查询出来,避免一次性将所有的数据全部查出来,查出来的部分数据通过前端的页面中不同的页数来展现出来 实现基础 mysql数据库中的查询语句中提供的limit关键字 该关键字可以限制查询的记录数,例如 s…

【026】基于vue+springboot的教务信息管理系统(含源码、数据库、课设报告、运行教程)

文章目录一、项目介绍二、源码获取一、项目介绍 基于Vue&#xff0b;springbootmysql的教务信息管理系统&#xff0c;UI设计主要采用element-ui&#xff0c;也使用了echarts做学生成绩的可视化界面&#xff0c;使用了xlsx表导入、导出数据&#xff0c;超级吊的教务管理系统&…

[操作系统笔记]页面置换算法

内容系听课复习所做笔记&#xff0c;图例多来自课程截图 常见的页面置换算法有&#xff1a;最优算法、随机算法、FIFO、LRU 随机算法顾名思义 FIFO(先进先出, First In First Out)算法&#xff1a;可能产生抖动现象和Belady现象 LRU&#xff08;最近最少使用算法&#xff09…

基于FFmpeg的视频播放器开发系列教程(二)

对于ffmpeg的架构介绍&#xff0c;请参考24岁“封神”雷霄骅的博客&#xff0c;他已离开江湖&#xff0c;但江湖仍有他的传说。 FFmpeg源代码结构图 - 编码&#xff1a;https://blog.csdn.net/leixiaohua1020/article/details/44226355 FFmpeg源代码结构图 - 解码&#xff1a;h…

【剪辑教程】如何给视频添加配音旁白,三种文字转语音方法教给你

视频剪辑完成&#xff0c;应该如何给视频配音&#xff1f;三种配音方法快来学 现在许多自媒体都居家创作短视频&#xff0c;那么给短视频配音的问题就摆着了面前&#xff0c;怎么给我们的短视频配音呢&#xff0c;接下来就教大家三种给视频添加配音的方法。 方法一&#xff1a…

Linux权限(1)

Linux权限(1) &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;Linux &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录Linux权限(1)1. Linux权限的概念1.1 Linux上的用…

NLP之文本分类模型调优(模型基于tensorflow1.14)

项目链接&#xff1a;https://pan.baidu.com/s/1yOu0DogWkL8WOJksJmeiPw?pwd4bsg 提取码: 4bsg 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 --来自百度网盘超级会员v4的分享 1.基于上一篇文章中的文本分类项目进行精度调优&#xff0c;提升模型准确率&…

CET-4 week10 语法

0基础入门 point 谓语中自带 助动词 Such as ,I do like computer (强调且默认带有这个属性 大多数情况隐藏状态) 所有时态都有被动 do ->donewill do -> will be donehave down ->have been downbe doing ->be being donedid -> been downhad down ->ha…

flink-cdc-connectors-release-2.3.0自己编译

最新的cdc是2.21仅支持flink 1.13* 1.14*&#xff0c;而flink已经有1.15版本&#xff1b;自己编译支持1.15 下载官方包 https://github.com/ververica/flink-cdc-connectors/releases/tag/release-2.3.0 我下载的是source包&#xff0c;大家也可以去下载源码 1、下载后我们只需…