深拷贝与浅拷贝,就是这么简单

2023-09-20 15:31:06

1.拷贝的概念

    在编程中,拷贝(或复制)是常见的操作之一。拷贝操作用于创建一个新对象或数据结构,使其具有与原对象或数据结构相同或部分相同的值。

    在进行拷贝操作时,常见的方式有浅拷贝和深拷贝。本文将重点讨论浅拷贝和深拷贝的概念、实现方式以及它们之间的区别。

2.浅拷贝

2.1. 浅拷贝的定义

    浅拷贝是指创建一个新对象,新对象的字段或属性与原对象相同。但对于原对象中的引用类型字段,浅拷贝只会复制其引用而不是实际的数据

2.2. 浅拷贝的实现方式

    当使用浅拷贝时,新对象和原始对象共享相同的数据引用,意味着它们指向内存中相同的对象

    在内存中,每个对象都被分配了一块内存空间。对象的字段存储在该内存空间中,并在字段中保存对其他对象的引用。当进行浅拷贝时,只复制了对象的字段值,而没有创建新的内存空间。

    假设有一个类 Person 如下:

class Person {
    private String name;
    private Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // Getter and setter methods...
}

    其中包含一个 Address 类型的字段 address。现在假设我们有一个原始对象 person1,其中的 address 字段引用了同一个 Address 对象:

Person person1 = new Person("Alice", new Address("City", "Street"));

    当进行浅拷贝时,通过调用 person1.clone() 方法可以得到一个新对象 person2:

Person person2 = person1.clone();

    此时,person1 和 person2 是两个独立的对象,但它们的 name 字段和 address 字段引用相同的内存地址。

2.3 在内存中:

在这里插入图片描述
在这里插入图片描述

     address 字段在 person1 和 person2 中都指向同一个内存地址,即一个共享的 Address 对象。这意味着,当修改了这个共享的 Address 对象时,无论是通过 person1 还是 person2,修改都会反映在两个对象中。

    所以浅拷贝的效果就是对象之间共享相同的引用,对其中一个对象的修改会影响到其他对象。而如果需要实现完全独立的副本,可以使用深拷贝来创建对象的拷贝,并在其中复制引用类型的字段,确保每个副本都有自己独立的对象引用。

    Java中,可以通过以下方式实现浅拷贝:
    实现Cloneable接口,并重写clone()方法。

3. 深拷贝

3.1. 深拷贝的定义

    深拷贝是指创建一个新对象,新对象的所有字段或属性都是原对象的副本。无论是基本类型还是引用类型,都会被复制到新对象中

3.2. 深拷贝的实现方式

    还是上面的例子,下面是深拷贝的实现:

class Person implements Cloneable {
    private String name;
    private Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person clonedPerson = (Person) super.clone();
        // 创建一个新的 Address 对象并复制原始对象的 address 字段值
        clonedPerson.address = new Address(this.address.getCity(), this.address.getStreet());
        return clonedPerson;
    }

    // Getter and setter methods...
}

class Address {
    private String city;
    private String street;

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    // Getter and setter methods...
}

    在这个示例中,我们通过覆盖 clone() 方法并在其中手动创建一个新的 Address 对象来实现深拷贝。这样就可以确保每个副本都有自己独立的 Address 对象。

3.3 在内存中

在这里插入图片描述
在这里插入图片描述

    可以看到,person1 和 person2 是两个独立的对象,并且它们的 address 字段引用了不同的内存地址,即各自拥有独立的 Address 对象。因此,对其中一个对象的修改不会影响到另一个对象。

    需要注意的是,在实现深拷贝时,不仅要复制引用类型的字段,还要确保引用类型的字段也实现了可复制(深拷贝)的逻辑,以防止仍然存在共享的引用。

4. 深拷贝与浅拷贝的区别

  • 浅拷贝只复制对象引用,不复制实际数据;
  • 深拷贝会递归复制所有嵌套的对象和数据。

二维表展示对比

深拷贝 (Deep Copy)浅拷贝 (Shallow Copy)
定义创建一个新的对象,将原始对象的所有属性进行递归复制到新对象中创建一个新的对象,将原始对象的属性的引用复制到新对象中
独立性深拷贝后的对象和原始对象是完全独立的,对其中一个对象的修改不会影响另一个对象浅拷贝后的对象和原始对象共享同一个属性对象,对其中一个对象的修改会影响另一个对象
对象关系深拷贝后的对象和原始对象没有任何关联浅拷贝后的对象和原始对象共享同一个属性对象
修改影响对原始对象的修改不会影响深拷贝后的对象对原始对象的修改会影响浅拷贝后的对象
对象复制深拷贝会递归复制所有属性对象,创建一个完全独立的对象浅拷贝只复制属性对象的引用,创建一个共享属性对象的新对象

    通过上述二维表的总结,可以清晰地看到深拷贝和浅拷贝的区别。
    深拷贝创建一个完全独立的对象,对原始对象的修改不会影响深拷贝后的对象;而浅拷贝创建一个共享属性对象的新对象,对原始对象的修改会影响浅拷贝后的对象。
    对于基本数据类型来说,两者一样,都会复制一份新的。

    如果还不太理解,举个例子看会不会好理解些:

  • 浅拷贝就像是在图书馆借阅一本书,并且与其他人共享同一本书。如果你标记了书中的某个页码或做了笔记,其他人看到的书也会有相同的变化。
  • 深拷贝则类似于每个人都有自己独立的书。你可以在你自己的书上做任何标记或笔记,而不会影响到其他人的书。

5. 原型模式与深浅拷贝的关系

    原型模式是一种创建型设计模式,它使用原型实例指定要创建的对象类型,并通过复制这个原型来创建新对象。在原型模式中,拷贝可以是浅拷贝或深拷贝,具体取决于如何实现原型对象的复制。

    在Java中,Cloneable接口就是原型模式的一种实现方式,它使用浅拷贝来复制对象。如果需要实现深拷贝,则需要在clone()方法中手动处理引用类型的字段或属性的拷贝。

    如果想有进一步的了解,可以参看我的上一篇博客
    链接: 设计模式之原型模式–超越实例化的魔法,从复制到创造的无限可能

6. 总结

  • 浅拷贝和深拷贝是常见的拷贝方式,用于创建对象副本。
  • 浅拷贝只复制引用。
  • 深拷贝复制整个对象及其嵌套的对象和数据。
更多推荐

基于SSM+Vue的汽车售票网站的设计与实现

末尾获取源码开发语言:JavaJava开发工具:JDK1.8后端框架:SSM前端:采用Vue技术开发数据库:MySQL5.7和Navicat管理工具结合服务器:Tomcat8.5开发软件:IDEA/Eclipse是否Maven项目:是目录一、项目简介二、系统设计设计原则三、系统项目截图客运班次管理会员充值管理购票记录管

笔记本、台式机、平板二合一?Mac、Win、Linux?

电脑选型根据日常使用的需求进行选择,笔记本、台式机、平板和二合一电脑我都有尝试过,目前而言,最适合我个人的是笔记本。笔记本如果你犹豫笔记本和台式机,选择笔记本;如果你犹豫笔记本和二合一电脑,选择笔记本。笔记本电脑比较中规中矩,是绝大多数场景下都可以作为第一或者备选方案的选择。在选择笔记本的时候,需要重点考虑如下几个因素

MySQL MHA

目录概述MHAMHA的组成MHA的特点搭建MySQLMHA配置主从复制1.关防火墙,安全机制2.修改Master、Slave1、Slave2节点的主机名3.在Master、Slave1、Slave2添加主机映射关系4.修改Master、Slave1、Slave2节点的Mysql主配置文件/etc/my.cnf5.在Ma

Mac 安装ZooKeeper+kafka基本使用

为什么Kafka依赖ZooKeeper?下面ZooKeeper基本介绍:1、基本功能ZooKeeper为分布式系统提供了一种配置管理的服务:集中管理配置,即将全局配置信息保存在ZooKeeper服务中,方便进行修改和管理,省去了手动拷贝配置的过程,同时还保证了可靠和一致性。2、命名服务在分布式系统中,经常需要对应用或者

大数据驱动业务增长:数据分析和洞察力的新纪元

文章目录引言大数据分析的重要性1.数据驱动的决策2.洞察力和预测3.个性化服务大数据分析的关键组成部分1.数据收集2.数据存储3.数据清洗和预处理4.数据分析和建模5.数据可视化数据驱动业务增长的案例1.亚马逊的个性化推荐2.谷歌的广告优化3.零售业的库存管理数据驱动文化的建立1.数据教育和培训2.数据可访问性3.数据

log4j2原理分析及漏洞复现CVE-2021-44228

文章目录log4j2原理分析及漏洞复现0x01log4j2简介Log4j2特点Log4j2组件的应用0x02CVE-2021-44228漏洞简介:漏洞适用版本漏洞原理lookup功能jndi解析器jndi是什么ldap服务RMI0x03攻击过程0x04漏洞复现漏洞环境1.访问靶机2.dns回显验证3.将bash反弹sh

深入解析:自己实现 MyBatis 底层机制的任务阶段1 - 读取配置文件与建立数据库连接

😀前言.本文将深入探讨如何在自己实现MyBatis底层机制的过程中完成第一个任务阶段,即读取配置文件并建立数据库连接。这一关键步骤是了解MyBatis内部工作原理的第一步,也是自定义MyBatis底层机制的基础。.在任务阶段1中,我们将从头开始创建必要的配置文件和Java类,逐步解析配置文件,获取数据库连接,以为后续

单片机论文参考:5、基于单片机的自动打铃系统

摘要本次设计中的LED数码管电子时钟电路采用24小时制记时方式,本次设计采用AT89C51单片机的扩展芯片和6个PNP三极管做驱动,由三块LED数码管构成的显示系统,与传统的基于8/16位普通单片机的LED显示系统相比较,本系统在不显著地增加系统成本的情况下,可支持更多的LED数码管稳定显示。设计采用AT98C51单片

详解SpringBoot的常用注解

详解SpringBoot的常用注解在SpringBoot中,注解是一种非常重要的编程方式,它可以简化代码,提高开发效率。本文将详细介绍SpringBoot中的常用注解,以及它们的使用方法和场景。1.@SpringBootApplication1.1概述@SpringBootApplication是SpringBoot应

单片机论文参考:3、基于单片机的电子万年历设计

摘要随着社会、科技的发展,人类得知时间,从观太阳、摆钟到现在电子钟,不断研究、创新。为了在观测时间,能够了解与人类密切相关的信息,比如星期、日期等,电子时钟诞生了,它集时间、日期、星期等功能于一身,具有读取方便、显示直观、功能多样、电路简洁等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。该电子时钟主要采用S

深入实现 MyBatis 底层机制的任务阶段 5- 开发和 Mapper 接口相映射的 MapperBean

😀前言在软件开发的不同阶段,数据库交互是一个至关重要的组成部分,特别是在构建数据库驱动的应用程序时。在任务的第五阶段,我们将继续深入研究与Mapper接口和MapperBean类相关的开发工作。这两个组件是将数据库操作方法映射到实际SQL查询的关键部分,它们为我们提供了管理和执行数据库操作的关键工具。.在之前的部分中

热文推荐