【JavaSE】Clonable?关于深拷贝与浅拷贝那些事儿咱们一次聊明白

news2024/10/6 2:28:06

💁 个人主页:黄小黄的博客主页
❤️ 支持我:👍 点赞 🌷 收藏 🤘关注
🎏 格言:All miracles start from sometime somewhere, make it right now.
本文来自专栏:JavaSE从入门到精通
在这里插入图片描述

文章目录

  • 1 在Java中如何实现对象的拷贝呢?
  • 2 浅拷贝与深拷贝引出
  • 3 如何实现深拷贝
  • 4 深拷贝实现完整代码
  • 写在最后


1 在Java中如何实现对象的拷贝呢?

Clonable 接口
  Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”。 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常。

我们来简单看一个拷贝的例子:

在该例子中,实现了对 person 对象的拷贝, Person 类中只含有一个属性:name

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * 克隆案例
 */
public class CloneDemo {

    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setName("黄小黄");
        Person p2 = p1.clone();
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        System.out.println("p1 = p2 ? " + (p1 == p2));
    }
}

class Person implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

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

    // 实现克隆Person对象
    @Override
    protected Person clone() {
        Person p = null;
        try {
            p = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

重点关注一下,clone方法的实现。测试结果如下:
在这里插入图片描述
这样一个简单的克隆demo就完成了!

此时我们 尝试修改p2对象中的name,执行如下代码后,得出结论p2的修改不会影响p1的内容。

        Person p1 = new Person();
        p1.setName("黄小黄");
        Person p2 = p1.clone();
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        System.out.println("p1 = p2 ? " + (p1 == p2));
        System.out.println("修改p2.name为懒羊羊");
        p2.setName("懒羊羊");
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());

在这里插入图片描述

然而真的是这样吗?如果Person类中含有引用数据类型的话,还能正常拷贝吗?我们继续往后看。


2 浅拷贝与深拷贝引出

 观察下面的代码,我们在上述克隆例子中的Person类,新增一个属性,该属性属于Wallet类,Wallet类中包含一个属性money。我们再来拷贝一下试试:
在这里插入图片描述
在这里插入图片描述
执行如下代码进行测试,尝试在克隆完成后,修改p2的wallet类型的money值:

    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setName("黄小黄");
        Wallet wallet = new Wallet();
        wallet.setMoney(100);
        p1.setMyMoney(wallet);
        Person p2 = p1.clone();
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
        System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
        System.out.println("p1 = p2 ? " + (p1 == p2));
        System.out.println("===修改p2的wallet属性为999===");
        wallet.setMoney(999);
        p2.setMyMoney(wallet);
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
        System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
    }

其结果如下:p2的修改竟然影响到了p1对象的wallet值!
在这里插入图片描述

⭐️ 从内存角度我们来看一看在修改wallet的时候发生了什么:

  1. 在使用clone进行克隆后,p1和p2为两个不同的对象,指向堆区的不同空间,其中,克隆出来的wallet属性,仅仅是拷贝了wallet的值(哈希地址),因此,虽然p1、p2为两个不同的对象,但是却指向了相同的空间。
    在这里插入图片描述
  2. 此时如果修改wallet的money100修改成999,则仅仅是改变了深蓝色区域的值。克隆出来的两个对象p1、p2中,wallet指向还是同一块内存,所以,通过p2对象修改wallet,也会影响到p1对象的wallet。
    在这里插入图片描述

 我们管这种更改克隆对象会影响原对象内容的拷贝方式称为 浅拷贝。 而将克隆后的两个对象,无论怎么修改,都不会影响另一个对象的拷贝方式,即拷贝后的两个对象互不影响的拷贝方式,称为 深拷贝。


3 如何实现深拷贝

 还是以刚刚的例子为例,我们的目的就是,让拷贝后的两个对象p1、p2互不影响,则 只要让拷贝后的两个对象各自的wallet也拷贝一份,不就能解决问题了!

示意图如下:
在这里插入图片描述

⭐️ 实现步骤:

  1. 让Wallet类实现Clonable接口,并实现clone方法。
    在这里插入图片描述
  2. 修改Person类实现的clone方法,实现person的克隆,同时也要克隆一份Wallet对象。
    在这里插入图片描述

4 深拷贝实现完整代码

按照上述步骤,实现Person类的深拷贝,完整代码如下:

class Person implements Cloneable {
    private String name;
    private Wallet myMoney;

    public Wallet getMyMoney() {
        return myMoney;
    }

    public void setMyMoney(Wallet myMoney) {
        this.myMoney = myMoney;
    }

    public String getName() {
        return name;
    }

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

    // 实现克隆Person对象
    @Override
    protected Person clone() {
        Person p = null;
        try {
            p = (Person) super.clone();
            myMoney = this.myMoney.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

class Wallet implements Cloneable {
    private int money;

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    // 实现克隆Wallet对象
    @Override
    protected Wallet clone() {
        Wallet w = null;
        try {
            w = (Wallet) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return w;
    }
}

测试代码如下:

public class CloneDemo {

    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setName("黄小黄");
        Wallet wallet = new Wallet();
        wallet.setMoney(100);
        p1.setMyMoney(wallet);
        Person p2 = p1.clone();
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
        System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
        System.out.println("p1 = p2 ? " + (p1 == p2));
        System.out.println("===修改p2的wallet属性为999===");
        wallet.setMoney(999);
        p2.setMyMoney(wallet);
        System.out.println("p1.name = " + p1.getName());
        System.out.println("p2.name = " + p2.getName());
        System.out.println("p1.wallet = " + p1.getMyMoney().getMoney());
        System.out.println("p2.wallet = " + p2.getMyMoney().getMoney());
    }
}

测试结果如下:
在这里插入图片描述


写在最后

🌟以上便是本文的全部内容啦,后续内容将会持续免费更新,如果文章对你有所帮助,麻烦动动小手点个赞 + 关注,非常感谢 ❤️ ❤️ ❤️ !
如果有问题,欢迎私信或者评论区!
在这里插入图片描述

共勉:“你间歇性的努力和蒙混过日子,都是对之前努力的清零。”
在这里插入图片描述

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

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

相关文章

新年新气象,100行 Python 代码制作动态鞭炮

放鞭炮贺新春,在我国有两千多年历史。关于鞭炮的起源,有个有趣的传说。 西方山中有焉,长尺余,一足,性不畏人。犯之令人寒热,名曰年惊惮,后人遂象其形,以火药为之。——《神异经》 当…

IU8689 单声道145W/75W立体声D类音频功放IC产品介绍

概要 IU8689E是-款单声道可输出145W,立体声2*75W D类音频功率放大器,这款器件在顶层设计了散热焊盘,焊盘上连接散热器后在供电电压24V的情况下,最大可以输出2x75W的连续功率;通过主从模式的设置可以让IU8689E实现无限级联&#x…

C# 线程的基本使用

一 多线程的概念 1 进程Process 2 线程Thread ① 线程中的指令:一个方法(委托) ② 线程中的数据:相关的对象; 3 System.Threading.Thread属性 4 System.Threading.Thread方法 5 线程的创建 1) Thread类…

初识Spring

目录 一: 为什么要学习? 二: 概述 三:Spring发展史 一: 为什么要学习? 可以最大程度地简化项目的开发大量公司在使用顶级的源码设计:spring框架源码设计非常优秀,在java开源项目中可以说是顶…

1.8T数据离奇消失之谜

编者按 数字化浪潮蓬勃兴起,企业面临的安全挑战亦日益严峻。 腾讯安全近期将复盘2022年典型的攻击事件,帮助企业深入了解攻击手法和应对措施,完善自身安全防御体系。 本篇是第六期,讲述了某企业NAS系统数据被删除,始…

SpringBoot 配合126邮箱实现邮件发送功能

126邮箱邮件发送授权码申请pom依赖邮件配置代码编写Bean与配置自动装载原始手工方式发送邮件发送纯文本的邮件发送包含HTML标签的邮件发送包含附件的邮件发送包含静态资源的文件测试SpringBoot 提供了系统级别邮箱服务,只需要导入一个邮箱启动器然后进行配置就可以使…

LabVIEW如何减少下一代测试系统中的硬件过时6

LabVIEW如何减少下一代测试系统中的硬件过时6 HAL Benefits When addressing obsolescence, HALs yield the benefits of lowermigration costs, faster migration time, higher code reuse, and easiermaintainability. Lower Migration Costs The act of designing a use…

如何pdf合并成一个?推荐3种方法

在企业处理多份合同文件或者财务报销时,经常会处理大量的PDF文件。因此为了更高效率办公,我们经常需要将多个pdf合并成一个。如何pdf合并成一个呢?给大家推荐3个方法。 1、Smallpdf工具 如何pdf合并成一个?推荐大家可以使用Small…

一篇canvas带你画出整个特效世界

目录 一,canvas是啥? 1.初识canvas 2.路径绘制 3.拆分画法 4.清除画布 5.绘制圆形笑脸 6.贝塞尔曲线 ①二次贝塞尔曲线 ②三次贝塞尔曲线 一,canvas是啥? Canvas是HTML5中新出的一个元素,我们可以在上面绘制…

为什么redis中提供hash数据类型?

目录 1.什么是哈希表?缺点是什么? 2.Redis的数据类型(type、encoding) 3.比较常用命令、使用场景、实现方式 1.什么是哈希表?缺点是什么? 把关键字key映射到表中记录的地址。映射关系是散列函数&#x…

图表控件LightningChart.NET 入门教程(六):许可证管理介绍(上)

LightningChart.NET SDK 是一款高性能数据可视化插件工具,由数据可视化软件组件和工具类组成,可支持基于 Windows 的用户界面框架(Windows Presentation Foundation)、Windows 通用应用平台(Universal Windows Platfor…

初学Java web(十)Filter 和 Listener

Filter和Listener 一.Filter 概念:Filter表示过滤器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。 过滤器一般完成一些通用的操作,比如:权限控…

Nuttx学习入门

Nuttx学习 NuttX 是一个实时操作系统 (RTOS),强调标准合规性和占用空间小。可从 8 位扩展到 64 位微控制器环境,NuttX 中的主要管理标准是 POSIX 和 ANSI 标准。 NuttX 的主要环境依赖性是 (1) GNU make,(2) bash 脚本,和 (3) L…

第三十四章 数论——高斯消元解线性方程组

第三十四章 数论——高斯消元解线性方程组一、高斯消元1、线性方程组2、高斯消元步骤(1)数学知识铺垫增广矩阵和阶梯矩阵初等变换(2)高斯消元步骤二、代码模板1、问题:2、代码一、高斯消元 1、线性方程组 我们在小学…

P3884 [JLOI2009]二叉树问题

题目 如下图所示的一棵二叉树的深度、宽度及结点间距离分别为: 深度:44宽度:44结点 8 和 6 之间的距离:88结点 7 和 6 之间的距离:33 其中宽度表示二叉树上同一层最多的结点个数,节点 u, vu,v 之间的距离…

算法工程师需要学习的基础

文章目录应该早点系统地了解算法工程师需要学习的东西的,B站上的up主:梁唐讲的很好,大家可以去看一下,只截了一部分图做一个记录

MySQL5.7 多主一从(多源复制)同步配置

主从复制有如下一些优势: 分担负载:对业务进行读写分离,减轻主库I/O负载,将部分压力分担到从库上,缩短客户查询响应时间。 增加健壮性:在主库出现问题时,可通过多种方案将从库设置为主库&#…

100%全国产龙芯2K1000设计方案

国产工业处理器,龙芯2K1000主板,100%全国产化方案 可实现100%国产元器件方案,国产处理器 信迈2k1000开发板采用龙芯 2k1000处理器,处理器集成 2 个 GS264 处理器核,主频 1GHz,64 位 DDR3 控制器&#xff…

Vue与VueComponent的内置关系

上一节讲到了 Vue.extend 与 VueComponent 的区别,这一节讲一讲 Vue 与 VueComponent的内置关系。 原型与原型链 这里需要用到原型与原型链中的知识点,具体文章链接在这里。js中的原型与原型链 这里只需要理解一个点,那就是构造函数的protot…

【idea2022.3】安装教程2022-12

教程目录教程简介所需环境和版本软件下载执行过程安装激活开始使用前的配置结束语教程简介 换电脑了,又经历了一遍软件和环境的安装,已经安装好了jdk和对应版本的maven,所以接下来该安装idea等软件了 所需环境和版本 系统:win1…