设计模式之原型模式(Prototype)

原型模式

如果已经有一个对象了,你想创建一个对象,而且对象里面的属性和已经存在的对象的属性差不多,就可以使用clone方法
克隆一个出来
在这里插入图片描述

实现原型模式需要实现标记型接口Cloneable -->标记型接口 : 里面没有需要实现的方法(空接口)
一般会重写clone方法 --> 如果只是重新clone方法,而没有实现Cloneable接口,调用时会报异常
一般用于一个对象的属性以及确定,需要产生很多相同或大部分属性相同的对象的时候
需要区分深克隆和浅克隆

java自带,Object类里面有个Object.clone()。也称为克隆模式
clone方法是protected native方法 C++实现 只能子类调用

浅克隆:
Object的clone,是在内存里面重新创建一个对象,copy过来原来对象的属性,如果是基本数据类型,拷贝的是值过去,
如果是引用数据类型,拷贝的是对象的内存地址,指向的是同一个对象,互相有影响
有没有方法把引用的对象也拷贝一份呢? --> 深克隆

/**
 * 浅克隆
 */

public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        // 克隆
        Person p2 = (Person)p1.clone();
        // 基本数据类型,值直接copy过来
        System.out.println(p2.age + " " + p2.score);
        System.out.println(p2.loc);
        // 引用数据类型,copy的是内存地址,指向的是同一个对象,所以相等
        System.out.println(p1.loc == p2.loc); // true
        // 如果p1对象的loc改变了,p2的loc也会改变
        p1.loc.street = "sh";
        System.out.println(p2.loc); // sh

    }
}

// 实现Cloneable接口
class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    // 实现clone方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Location {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }
}

深克隆:
引用的对象Location也实现Cloneable接口以及clone方法,在clone Person对象的时候将原来对象的loc也clone一份出来,
然后新clone出来 的对象的loc引用指向clone处理的loc

/**
 * 深克隆的处理
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p2.age + " " + p2.score); // 8 100
        System.out.println(p2.loc);
        // 不是同一个对象了
        System.out.println(p1.loc == p2.loc); // false
        p1.loc.street = "sh";
        // 改变p1的loc不好影响p2
        System.out.println(p2.loc); // bj
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        // clone一份新的对象出来
        Person p = (Person)super.clone();
        // 将原来的loc,也clone一份出来,新对象的loc指针指向clone处理的loc对象
        p.loc = (Location)loc.clone();
        return p;
    }
}
// 被引用的对象实现Cloneable接口以及clone方法
class Location implements Cloneable {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

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

引用数据类型,在深克隆的时候,是需要被克隆一份的,上面的String也是引用数据类型,clone的时候也需要将它克隆一份吗?
不需要。
String类型放在了常量池里面,比如说第一个Person对象里面的String指向的是常量池里面的"bj",clone出来的对象也是指向的常量池里面的"bj",
如果第一个Person对象里面的String变成了"sh",是不会影响第二个对象的,因为只是第一个对象里面的String指向了常量池里面的"sh",被克隆出来
的对象还是指向的"bj"。

/**
 * String需要进一步深克隆吗?
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p2.age + " " + p2.score);
        System.out.println(p2.loc);

        System.out.println(p1.loc == p2.loc); // false
        p1.loc.street = "sh";
        System.out.println(p2.loc);

        p1.loc.street.replace("sh", "sz");
        // 改变p1里面的string,不好影响p2
        System.out.println(p2.loc.street); // bj
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person)super.clone();
        p.loc = (Location)loc.clone();
        return p;
    }
}

class Location implements Cloneable {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

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

下面的例子中,String用的是StringBuilder,Person 1和Person 2里面的String都是指向的同一个StringBuilder,所以
当Person 1的String改变之后,Person 2的String也会改变。
怎么解决? --> 深克隆的时候,也需要把StringBuilder克隆一份

/**
 * String需要进一步深克隆吗?
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        // loc 经过clone之后指向的不是同一个对象
        System.out.println("p1.loc == p2.loc? " + (p1.loc == p2.loc)); // false
        // p1和p2里面的String指向的是同一个StringBuilder,所以当p1里面的String改变的时候,
        // p2的也会改变
        p1.loc.street.reverse();
        System.out.println(p2.loc.street); // jb
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location(new StringBuilder("bj"), 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person)super.clone();
        p.loc = (Location)loc.clone();
        return p;
    }
}

class Location implements Cloneable {
    StringBuilder street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(StringBuilder street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

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

深克隆用的少,面试也问的少,了解就行

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL学习之增删改查

文章目录 数据库数据类型建表create table插入数据insert into查询数据select from修改数据update set删除数据delete from备份ctas结果插入iis截断表 truncate table修改表结构alter table添加注释 注:本文的SQL语法是基于Oracle数据库操作的,但是基本的…

猫罐头如何选择?最受欢迎的5款猫罐头推荐!新手养猫速看!

对于一个刚入门的养猫新手来说,面对市面上琳琅满目的猫罐头选择确实让人头大。我们总想选到营养价值高的罐头,但又怕猫咪不喜欢吃,还担心选到不安全的产品。 作为家里有5只猫猫的铲屎官来说,养猫的这几年可以说是血泪史了&#x…

保姆级vue-pdf的使用过程

第一步 引入vue-pdf npm install --save vue-pdf 第二步 按照需求我们慢慢进行 01.给你一个pdf文件的url&#xff0c;需要在页面渲染 代码 <template><div><pdfref"pdf":src"url"></pdf></div> </template> <…

scDrug:从scRNA-seq到药物反应预测

scRNA-seq技术允许在转录组水平上对数千个细胞进行测量。scRNA-seq正在成为研究肿瘤微环境中细胞成分及其相互作用的重要工具。scRNA-seq也被用于揭示肿瘤微环境模式与临床结果之间的关联&#xff0c;并在复杂组织中剖析药物治疗的细胞特异性效应。scRNA-seq的最新进展推动了疾…

postswigger 靶场(CSRF)攻略-- 3.令牌验证

靶场地址&#xff1a; https://portswigger.net/web-security/csrf 令牌(token) 验证取决于令牌(token) 的存在 题目中已告知易受攻击的是电子邮件的更改功能&#xff0c;而目标是利用 csrf 漏洞更改受害者的电子邮件地址&#xff0c;最后给出了登录凭据&#xff1a;wiener:pet…

SaaS 电商设计 (三) 如何做大促压测

一.背景&目标 1.1 常见的压测场景 电商大促:一众各大厂的促销活动场景,如:淘宝率先推出的天猫双11,而后京东拉出的京东 618 .还是后续陆陆续续的一些年货节, 3.8 女神节等等.都属于一些常规的电商大促 票务抢购:常见的如承载咱们 80,90 青春回忆的 Jay 的演唱会,还有普罗…

LLM 大模型向量数据库技术架构浅析

▼最近直播超级多&#xff0c;预约保你有收获 近期直播&#xff1a;《LLM 大模型向量数据库技术架构剖析和应用案例实战》 —1— AI 智能时代&#xff0c;开发者需要一个真正的向量数据库吗&#xff1f; 答案很简单&#xff0c;这取决于开发者的应用场景。举个例子&#xff0c;…

内网穿透的应用-如何使用CFImagehost搭建简洁易用的私人图床并公网访问

文章目录 1.前言2. CFImagehost网站搭建2.1 CFImagehost下载和安装2.2 CFImagehost网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

pdf增强插件 Enfocus PitStop Pro 2022 mac中文版功能介绍

Enfocus PitStop Pro mac是一款 Acrobat 插件&#xff0c;主要用于 PDF 预检和编辑。这个软件可以帮助用户检查和修复 PDF 文件中的错误&#xff0c;例如字体问题、颜色设置、图像分辨率等。同时&#xff0c;Enfocus PitStop Pro 还提供了丰富的编辑工具&#xff0c;可以让用户…

响应式工作范文作文学习参考资料网站模板源码

模板信息&#xff1a; 模板编号&#xff1a;29083 模板编码&#xff1a;UTF8 模板分类&#xff1a;博客、文章、资讯、其他 适合行业&#xff1a;工作范文类企业 模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#xff0c;原创设计、手工书写DIVC…

docker小技能

文章目录 I 预备知识Docker组成命名空间 (进程隔离)II 常用命令2.1 案例:流水线docker 部署2.2 删除没有使用的镜像2.3 shell 不打印错误输出2.4 阿里云流水线/jenkins忽略shell步骤中的报错https://www.runoob.com/docker/docker-architecture.html I 预备知识 Docker组成…

【vue】0到1的常规vue3项目起步

创建项目并整理目录 npm init vuelatestjsconfig.json配置别名路径 配置别名路径可以在写代码时联想提示路径 {"compilerOptions" : {"baseUrl" : "./","paths" : {"/*":["src/*"]}} }elementPlus引入 1. 安装e…

openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程

文章目录 openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程122.1 创建并执行涉及加密列的函数/存储过程 openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程 密态支持函数/存储过程当前版本只支持sql和P…

【工程实践】Docker使用记录

前言 服务上线经常需要将服务搬到指定的服务器上&#xff0c;经常需要用到docker&#xff0c;记录工作中使用过dcoker指令。 1.写Dockerfile 1.1 全新镜像 FROM nvidia/cuda:11.7.1-devel-ubuntu22.04ENV WORKDIR/data/Qwen-14B-Chat WORKDIR $WORKDIR ADD . $WORKDIR/RUN ap…

【数组方法reduce】reduce细讲以及模拟重写其他数组扩展方法

学习关键语句: Array.reduce Array.prototype.reduce reduce方法 重写 reduce 方法 1. 写在前面 很多同学 ( 指我自己 ) 在学习其他数组扩展方法时都没那么困难 , 但是到了 reduce 方法时就会显得蠢蠢的 , 所以今天就赶紧将这个方法讲个明白 其实所有的数组扩展方法本质上都…

数据结构 顺序表和链表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串.. 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

2023年【汽车驾驶员(高级)】证考试及汽车驾驶员(高级)实操考试视频

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 汽车驾驶员&#xff08;高级&#xff09;证考试考前必练&#xff01;安全生产模拟考试一点通每个月更新汽车驾驶员&#xff08;高级&#xff09;实操考试视频题目及答案&#xff01;多做几遍&#xff0c;其实通过汽车…

【备忘】在Nginx服务器安装SSL证书

您可以在Nginx或Tengine服务器上安装SSL证书&#xff0c;实现通过HTTPS安全访问Web服务器。本文介绍如何为Nginx或Tengine服务器安装SSL证书。 重要 本文以CentOS 8.0 64位操作系统、Nginx 1.14.2为例介绍。不同版本的操作系统或Web服务器&#xff0c;部署操作可能有所差异&a…

2020年12月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 下面哪个区域是“舞台区”? A:A B:B C:C D:D 答案:B 第2题 下面哪段程序可以随机切换三个背景? A: B: C: D:

私域电商:构建商业新模式的必要性

随着互联网的快速发展&#xff0c;传统的电子商务模式已经无法满足企业对于个性化、精准化服务的需求。在这样的背景下&#xff0c;私域电商应运而生&#xff0c;为企业提供了新的商业机会和增长点。本文将探讨私域电商的必要性及其构建商业新模式的影响。 一、私域电商的概念 …