浅克隆和深克隆的详细教程~

news2026/4/8 18:24:24

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

传统创建重复对象的方法:

package originModel;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
class Teacher implements Cloneable{
    private String name;
    private Student student;
}
package originModel;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int age;
    private String name;
}
package originModel;

public class test1 {
    public static void main(String[] args) {
        Teacher teacher1=new Teacher();
        teacher1.setName("张三");
        teacher1.setStudent(new Student(19,"小明"));
        Teacher teacher2=new Teacher(teacher1.getName(),teacher1.getStudent());
        System.out.println(teacher1);
        System.out.println(teacher2);
    }
}

输出如下所示:

Teacher(name=张三, student=Student(age=19, name=小明))
Teacher(name=张三, student=Student(age=19, name=小明))

传统方式虽然比较好理解而且也容易操作,但是在创建新的对象时,总是需要获取原始对象的属性,如果创建的对象比较复杂,这种方式效率很低。

通过原型模式创建重复对象的方法:本质“克隆”

浅克隆:

实现步骤:

原型对象实现
1:实现Cloneable接口
2:实现clone方法
package originModel;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
class Teacher implements Cloneable {
    private String name;
    //student类为Teacher类的成员变量
    private Student student;
    @Override
    public Object clone() throws CloneNotSupportedException {
        //浅克隆
        return super.clone();
    }

测试类:

package originModel;

public class test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher1=new Teacher();
        teacher1.setName("张三");
        teacher1.setStudent(new Student(19,"小明"));
        Teacher teacher2= (Teacher) teacher1.clone();
        System.out.println(teacher1);
        System.out.println(teacher2);
    }
}

输出如下所示:

Teacher(name=张三, student=Student(age=19, name=小明))
Teacher(name=张三, student=Student(age=19, name=小明))

通过克隆我们得到了和原型对象值完全相同的克隆对象,如果我们修改克隆对象的值是否会影响到原型对象呢?

验证如下所示:

package originModel;
public class test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher1=new Teacher();
        teacher1.setName("张三");
        teacher1.setStudent(new Student(19,"小明"));
        Teacher teacher2= (Teacher) teacher1.clone();
        System.out.println(teacher1.getStudent());
        System.out.println(teacher2.getStudent());
        System.out.println(teacher1.getStudent()==teacher2.getStudent());
        // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象?
        teacher2.getStudent().setName("李华");
        teacher2.getStudent().setAge(20);
        System.out.println(teacher1.getStudent());
        System.out.println(teacher2.getStudent());
        System.out.println(teacher1.getStudent()==teacher2.getStudent());
        // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象?
        teacher2.setName("李红");
        System.out.println(teacher1.getName());
        System.out.println(teacher2.getName());
        System.out.println(teacher1.getName()==teacher2.getName());
    }
}

输出如下所示:

修改克隆对象中引用数据类型的值影响到了原型对象,而修改克隆对象中基本数据类型的值并没有影响到原型对象

Student(age=19, name=小明)
Student(age=19, name=小明)
true
Student(age=20, name=李华)
Student(age=20, name=李华)
true
张三
李红
false

出现上述结果的原因如下所示:

克隆对象中的基本数据类型对象是直接存储在克隆对象自身的内存空间中而不是引用原型对象的内存空间当修改克隆对象中的基本数据类型对象的值时,只是修改了克隆对象自身的值,不会影响原型对象的值。

克隆对象中的引用数据类型对象则是引用了原型对象的内存地址,它们指向同一个对象当修改克隆对象中的引用数据类型对象的值时,实际上是修改了原型对象的值,因为它们共享同一块内存空间。所以修改克隆对象中引用数据类型对象的值会导致原型对象的值发生改变。

如果我们不希望对克隆对象的修改影响到原始对象,那么我们可以使用深克隆实现:

深克隆:

是指创建一个新的对象这个新对象与原对象是完全独立的它们在内存中的地址是不同的。因此,修改克隆对象中引用类型的值不会影响原型对象。

方式 1:重写clone方法,依次调用成员变量的clone方法

如下所示:

package originModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
class Teacher implements Cloneable{
    private String name;
    private Student student;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Teacher teacher= (Teacher) super.clone();//该行实现的依然是浅克隆
        //克隆引用对象
        Student student1= (Student) teacher.getStudent().clone();
        teacher.setStudent(student1);
        return teacher;
    }
}

既然要调用引用对象Student的克隆方法,那么Student类也要实现对应的Cloneable接口和clone方法

package originModel;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Cloneable{
    private int age;
    private String name;

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

修改测试类如下所示:

package originModel;
public class test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher1=new Teacher();
        teacher1.setName("张三");
        teacher1.setStudent(new Student(19,"小明"));
        Teacher teacher2= (Teacher) teacher1.clone();
        System.out.println(teacher1.getStudent());
        System.out.println(teacher2.getStudent());
        System.out.println(teacher1.getStudent()==teacher2.getStudent());
        // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象?
        teacher2.getStudent().setName("李华");
        teacher2.getStudent().setAge(20);
        System.out.println(teacher1.getStudent());
        System.out.println(teacher2.getStudent());
        System.out.println(teacher1.getStudent()==teacher2.getStudent());
        // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象?
        System.out.println(teacher1.getName());
        System.out.println(teacher2.getName());
        System.out.println(teacher1.getName()==teacher2.getName());
        teacher2.setName("李红");
        System.out.println(teacher1.getName());
        System.out.println(teacher2.getName());
        System.out.println(teacher1.getName()==teacher2.getName());
    }
}

输出如下所示:

Student(age=19, name=小明)
Student(age=19, name=小明)
false
Student(age=19, name=小明)
Student(age=20, name=李华)
false
张三
张三
true
张三
李红
false

但是如果一个类中包含的引用类型对象有很多,我们使用这种方式的效率就非常低

方式2:使用Json工具,比如GSON对象序列化和反序列化实现深克隆

第一步导入对应的依赖:

<dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
</dependency>

如下所示:

package originModel;

import com.google.gson.Gson;

public class test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher1=new Teacher();
        teacher1.setName("张三");
        teacher1.setStudent(new Student(19,"小明"));
        Gson gson=new Gson();
        //对象的序列化
        String json=gson.toJson(teacher1);
        //对象的反序列化
        Teacher teacher2=gson.fromJson(json,Teacher.class);
        System.out.println(teacher1.getStudent());
        System.out.println(teacher2.getStudent());
        System.out.println(teacher1.getStudent()==teacher2.getStudent());
        // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象?
        teacher2.getStudent().setName("李华");
        teacher2.getStudent().setAge(20);
        System.out.println(teacher1.getStudent());
        System.out.println(teacher2.getStudent());
        System.out.println(teacher1.getStudent()==teacher2.getStudent());
        // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象?
        System.out.println(teacher1.getName());
        System.out.println(teacher2.getName());
        System.out.println(teacher1.getName()==teacher2.getName());
        teacher2.setName("李红");
        System.out.println(teacher1.getName());
        System.out.println(teacher2.getName());
        System.out.println(teacher1.getName()==teacher2.getName());
    }
}

输出如下所示:

对克隆对象(teacher2)的修改没有影响到原始对象(teacher1)

Student(age=19, name=小明)
Student(age=19, name=小明)
false
Student(age=19, name=小明)
Student(age=20, name=李华)
false
张三
张三
false
张三
李红
false

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

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

相关文章

VulnHub Metasploitable-2

一、信息收集 nmap扫描 访问80端口 二、漏洞利用 1.漏洞一 1.vsftpd 2.3.4&#xff08;CVE-2011-2523&#xff09; 2.msf msf6 > search vsftpd msf6 > use 0 msf6 exploit(unix/ftp/vsftpd_234_backdoor) > set rhosts 192.168.103.189 msf6 exploit(unix/ftp/vs…

ATV32变频器在堆垛机应用

一、机型介绍&#xff1a; 目前国内物流行业发展速度很快&#xff0c;特别是在自动仓库这一块&#xff0c;自动仓库用的最多是堆垛机&#xff0c;自动仓库目前驱动用得基本上变频器。品牌基本是丹佛斯、日系及其他等重载系列变频器。设备主要包括&#xff1a;提升机、货叉及行…

【Java题】输出基本数据类型的最大值和最小值,以及float和double的正无穷大值和负无穷大值

一&#xff1a;代码 public class Test {public static void main(String[] args) {//输出byte型的最大值与最小值System.out.println(Byte.MAX_VALUE);System.out.println(Byte.MIN_VALUE);//输出short型的最大值与最小值System.out.println(Short.MAX_VALUE);System.out.pri…

2023-2024 年最佳 6 款数据恢复软件免费在线下载

如果您正在寻找在线数据恢复工具来帮助自己摆脱数据丢失的麻烦&#xff0c;这篇文章可以为您提供帮助。我们讲解如何免费在线恢复数据&#xff0c;并从兼容性、适用性、易用性、价格等角度分享了市场上六款著名的数据恢复软件。每个在线恢复工具都是安全的&#xff0c;并且可以…

植物大战僵尸 forMac/Windows系统中文版:一场惊心动魄的生存之战

在充满惊喜与挑战的《植物大战僵尸》游戏中&#xff0c;一场奇妙的生存之战正等待着你。为了保护你的大脑&#xff0c;你必须组建一支植物军队&#xff0c;利用各种独特的植物和能力&#xff0c;抵御一波又一波的僵尸大军。现在就让我们深入了解这款引人入胜的游戏&#xff0c;…

【C++】Linux下如何查看opencv的版本

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

Bootstrap的咖啡网站实例代码阅读笔记

目录 01-index.html的完整代码02-图片可以通过类 rounded-circle 设置为圆形显示03-<li class"nav-item mt-1 a">中&#xff0c;类mt-1是什么意思&#xff1f;类a又是什么意思&#xff1f;04-href"javascript:void(0);"是什么意思&#xff1f;05-类f…

Java 浅拷贝会带来的问题

Java 浅拷贝会带来的问题 一&#xff0c;常见问题 Java 中的浅拷贝是指在对象拷贝时&#xff0c;只复制对象的引用&#xff0c;而不是对象本身。这意味着浅拷贝会导致多个对象共享同一块内存空间&#xff0c;当一个对象修改共享内存时&#xff0c;其他对象也会受到影响。 下…

Ubuntu下使用Docker的简单命令

1&#xff1a;要在Ubuntu下使用Docker首先需要提权&#xff0c;Ubuntu下root是没有密码的。注意前导符的变化$是普通用户&#xff0c;#是管理员。 sudo -i2&#xff1a;运行一个容器。-d是后台运行&#xff0c;-p是把http的端口号由80变成8080。 docker run -d -p 8080:80 ht…

php之 角色的权限管理(RBAC)详解

RBAC&#xff08;Role-based access control&#xff09;是一种常见的权限管理模型&#xff0c;通过将用户分配至特定的角色&#xff0c;以及为角色分配访问权限&#xff0c;实现了权限管理的目的。以下是关于RBAC的详细解释&#xff1a; 角色&#xff1a;RBAC模型的核心是角色…

[RISC-V]verilog

小明教IC-1天学会verilog(7)_哔哩哔哩_bilibili task不可综合&#xff0c;function可以综合

FL Studio21水果编曲软件如何切换成官方中文版

FL studio又被国内网友称之为水果音乐制作软件21版本&#xff0c;是Image-Line公司成立23周年而发布的一个版本&#xff0c;FL studio中文版是目前互联网上最优秀的完整的软件音乐制作环境或数字音频工作站&#xff0c;FL Studio包含了编排&#xff0c;录制&#xff0c;编辑&am…

面试题:百万数据的导入导出解决方案,怎么设计?

文章目录 前景1 传统POI的的版本优缺点比较HSSFWorkbookXSSFWorkbookSXSSFWorkbook 2 使用方式哪种看情况3 百万数据导入导出&#xff08;正菜&#xff09;想要解决问题我们首先要明白自己遇到的问题是什么&#xff1f;解决思路&#xff1a;3.1 EasyExcel 简介3.2 300w数据导出…

P1494 [国家集训队] 小 Z 的袜子

这一题是一个关于多次查询区间状态的一个问题&#xff0c;暴力肯定会超限&#xff0c;但是可以用莫队来优化暴力。 莫队的思想就是&#xff0c;用上一个区间的状态来更新当前区间的状态。 问题就是状态怎么更新以及求出当前区间的状态、也就是有多少对相同的袜子以及总共有多…

禁止chrome浏览器更新方式

1、禁用更新服务 WinR调出运行&#xff0c;输入services.msc&#xff0c;进入服务。 在服务中有两个带有Google Update字样&#xff0c;双击打开后禁用&#xff0c;并把恢复选项设置为无操作。 2、删除计划任务 运行taskschd.msc&#xff0c;打开计划任务程序库&#xff0c;在…

uniapp 中添加 vconsole

uniapp 中添加 vconsole 一、安装 vconsole npm i vconsole二、使用 vconsole 在项目的 main.js 文件中添加如下内容 // #ifdef H5 // 提交前需要注释 本地调试使用 import * as vconsole from "vconsole"; new vconsole() // 使用 vconsole // #endif三、成功

Redis中的数据类型及与Mysql数据库同步方法

1.Redis中的数据类型 Redis中的数据类型包括&#xff1a; 排行榜应选用有序集合Zset&#xff0c;原因是排行榜既要去重&#xff0c;也要排序&#xff0c;用这种结构最为合适。 2.Redis和MySQL之间的同步常见方法 要实现Redis和MySQL之间的同步&#xff0c;常见方法包括&…

为什么网上的流量卡都有禁发地区呢?流量卡管控地区整理!

在网上购买过流量卡的朋友应该都知道&#xff0c;但凡是运营商推出的大流量优惠套餐&#xff0c;在套餐详情中都是有禁发地区&#xff0c;只不过每张卡的禁发地区不同而已。 设置禁发地区的主要目的还是为了防止一些电信诈骗案件的发生&#xff0c;或者违法违规利用电话卡的情…

Windows-Oracle11g 安装详解-含Navicate远程连接配置 -本地监听设置及更换navicate环境指向的oci.dll

文章目录 1 下载地址&#xff1a;2 安装2.1 解压缩运行setup2.2 修改配置文件2.3 一直选择默认&#xff0c;直到设置口令2.4 Oracle服务启动 3 登录Oracle4 解锁普通用户scott5 简化连接&#xff08;可做可不做&#xff09;5.1 修改配置文件5.2 添加内容 6 配置本地监听6.1 修改…

正点原子嵌入式linux驱动开发——Linux RTC驱动

RTC也就是实时时钟&#xff0c;用于记录当前系统时间&#xff0c;对于Linux系统而言时间是非常重要的&#xff0c;就和使用Windows电脑或手机查看时间一样&#xff0c;在使用Linux设备的时候也需要查看时间。本章就来学习一下如何编写Linux下的RTC驱动程序。 Linux内核RTC驱动…