深拷贝和浅拷贝的区别

article/2025/9/17 4:33:03

首先,明确一点深拷贝和浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个,而对于对象或者引用数据来说在进行浅拷贝时,只是将对象的引用复制了一份,也就内存地址,即两个不同的变量指向了同一个内存地址,那么在改变任一个变量的值都是该变这个内存地址的所存储的值,所以两个变量的值都会改变。

一、clone()方法

在Java中是用clone()方法实现深拷贝的,比如以下代码在Java中是很常见的

Person p = new Person(23, "zhang");Person p1 = p;System.out.println(p);System.out.println(p1);

这段代码的打印结果为:

testclone.Person@2f9ee1ac
testclone.Person@2f9ee1ac 

可以看出两者打印的地址是相同的,既然地址相同那就代表两者是同一个对象,而p和p1都只是引用罢了。代码执行后,内存中的情况

 

而下面这段代码才是真的克隆了一个对象

Person p = new Person(23, "zhang");Person p1 = (Person) p.clone();System.out.println(p);System.out.println(p1)

打印的结果为:

testclone.Person@2f9ee1ac
testclone.Person@67f1fba0

从结果来看两者不是同一个地址,那自然就不是同一个对象,而内存情况如下

 二、深拷贝和浅拷贝

在上面的代码中Person中有两个成员变量,分别是name和age, name是String类型, age是int类型。代码非常简单,如下所示:

public class Person implements Cloneable{private int age ;private String name;public Person(int age, String name) {this.age = age;this.name = name;}public Person() {}public int getAge() {return age;}public String getName() {return name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return (Person)super.clone();}
}

 Person类的两个成员变量中,age是基本数据类型,关于基本数据类型的拷贝在上面就已经说过了,但是name是引用数据类型,它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:

下面我们通过代码来验证:如果两个Person对象的name的地址值相同, 说明两个对象的name都指向同一个String对象, 也就是浅拷贝, 而如果两个对象的name的地址值不同, 那么就说明指向不同的String对象, 也就是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:

Person p = new Person(23, "zhang");Person p1 = (Person) p.clone();String result = p.getName() == p1.getName() ? "clone是浅拷贝的" : "clone是深拷贝的";System.out.println(result);

打印的结果为:

clone是浅拷贝

也就是clone方法本身是一个浅拷贝的方法,而上一个代码我们又得出clone是深拷贝,那么是我前面写错了吗?那当然不是,clone方法本身是浅拷贝的,但是我们可以通过重写该方法来实现深拷贝 ,所以要实现深拷贝我们就需要实现Cloneable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的,再次以下面的代码验证:

static class Body implements Cloneable{public Head head;public Body() {}public Body(Head head) {this.head = head;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}}static class Head /*implements Cloneable*/{public  Face face;public Head() {}public Head(Face face){this.face = face;}} public static void main(String[] args) throws CloneNotSupportedException {Body body = new Body(new Head());Body body1 = (Body) body.clone();System.out.println("body == body1 : " + (body == body1) );System.out.println("body.head == body1.head : " +  (body.head == body1.head));}

 在上述代码中有两个主要的类,Body和Face,Body类中又组合了Face对象,在对Body进行clone时,它所组合的Face对象时浅拷贝,其打印结果可以很好的证明这一点:

body == body1 : false
body.head == body1.head : true

如果要使Body对象在clone时进行深拷贝, 那么就要在Body的clone方法中,将源对象引用的Head对象也clone一份。

static class Body implements Cloneable{public Head head;public Body() {}public Body(Head head) {this.head = head;}@Overrideprotected Object clone() throws CloneNotSupportedException {Body newBody =  (Body) super.clone();newBody.head = (Head) head.clone();return newBody;}}static class Head implements Cloneable{public  Face face;public Head() {}public Head(Face face){this.face = face;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}} public static void main(String[] args) throws CloneNotSupportedException {Body body = new Body(new Head());Body body1 = (Body) body.clone();System.out.println("body == body1 : " + (body == body1) );System.out.println("body.head == body1.head : " +  (body.head == body1.head));}

 

打印结果为:
body == body1 : false
body.head == body1.head : false

由此可见, body和body1内的head引用指向了不同的Head对象, 也就是说在clone Body对象的同时, 也拷贝了它所引用的Head对象, 进行了深拷贝。
从以上的内容中我们可以知道,一个对象要实现深拷贝,就要实现Cloneable接口,重写clone()方法,并在clone()方法中将对象引用的其他对象也要clone一份,这就要求被引用的对象也要实现Cloneable接口,并实现clone()方法。

而按照这个结论,Body类中组合了Head类,而Head类中又组合了Face类,在对Body对象进行深拷贝时,会拷贝其中的Head类,而这时默认执行的时浅拷贝,也就是说其中的Face对象并不会被拷贝,代码如下:

static class Body implements Cloneable{public Head head;public Body() {}public Body(Head head) {this.head = head;}@Overrideprotected Object clone() throws CloneNotSupportedException {Body newBody =  (Body) super.clone();newBody.head = (Head) head.clone();return newBody;}}static class Head implements Cloneable{public  Face face;public Head() {}public Head(Face face){this.face = face;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}} static class Face{}public static void main(String[] args) throws CloneNotSupportedException {Body body = new Body(new Head(new Face()));Body body1 = (Body) body.clone();System.out.println("body == body1 : " + (body == body1) );System.out.println("body.head == body1.head : " +  (body.head == body1.head));System.out.println("body.head.face == body1.head.face : " +  (body.head.face == body1.head.face));}

打印结果为:

body == body1 : false
body.head == body1.head : false
body.head.face == body1.head.face : true

 

那我们可以想一想,这对Body对象来说,到底是不是深拷贝,其实是可以算的,因为Body对象中的引用对象是拷贝了的(这段代码中只有Head对象的引用),也就是说两个独立的Body对象中的Head引用指向了两个独立的Head对象,但对于Head对象来说,其Face引用指向了同一个Face对象。所以严格来说这是一种不彻底的深拷贝,而若是想要彻底的深拷贝,就要保证该对象的所有引用对象的类型都要去实现Cloneable接口,实现clone方法。 

参考文献:https://blog.csdn.net/zhangjg_blog/article/details/18369201

 

 

 


http://chatgpt.dhexx.cn/article/isuwtPQf.shtml

相关文章

浅拷贝与深拷贝的区别

浅拷贝与深拷贝 一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。 基本数据类型的特点:直接存储在栈(stack)中的数据 引用数据类型的特点:存储的是该对象在栈中引用,真实的…

堆、栈和队列

1. 堆 堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。堆是指程序运行时申请的动态内存,而栈只是指一种使用堆的方法(即先进后出)。 2. 栈(stack&…

堆、栈和队列的区别

目录 数据结构中的堆、栈和队列 内存申请中的堆和栈 一个C/C程序占用的内存如下: 申请内存后的响应 申请大小的限制 申请效率的比较 堆和栈中的存储内容 数据结构中的堆、栈和队列 堆:堆是一种经过排序的树形数据结构,每个结点都有…

栈和队列

文章目录 栈栈操作队列队列操作双端队列双端队列操作 栈 栈(stack),有些地方称为堆栈,是一种容器,可存入数据元素、访问元素、删除元素,它的特点在于只能允许在容器的一端(称为栈顶端指标&…

栈和队列简介

栈和队列简介 栈和队列是两种常用的数据结构,它们的数据是按线性结构存储的,因此,栈和队列也属于线性表。 栈和队列的数据可以存储在一个顺序表里,也可以存储在一个链表里,只要满足线性存储结构就行。只对数据的线性…

栈和队列基本操作

栈和队列 一、栈栈是什么栈的实现栈的基本操作栈类型的定义初始化栈检查栈是否为满入栈出栈获取栈顶元素检测栈是否为空测试函数 完整代码Stack.hStack.cmain.c 二、队列队列是什么队列的实现队列类型的定义申请一个节点初始化队列队尾入队列队头出队列获取队列头部元素获取队列…

C#队列和栈的使用

一、队列 队列是其元素以先进先出&#xff08;Firstin,Firstout,FIFO&#xff09;的方式来处理的集合。先放入队列中的元素会先读取。队列使用System.Collections.Generic命名空间中的泛型类Queue<T>实现。 队列的成员 Count&#xff1a;Count属性返回队列中元素个数。…

栈和队列经典面试题

目录 1、括号匹配问题 2、用队列实现栈 3、用栈实现队列 4、设计循环队列 1、括号匹配问题 链接直达&#xff1a; 有效的括号 题目&#xff1a; 思路&#xff1a; 做题前&#xff0c;得先明确解题方案是啥&#xff0c;此题用栈的思想去解决是较为方便的&#xff0c;栈明确指…

栈stack和队列

栈和队列 一 栈和队列二 栈三.队列 一 栈和队列 栈和队列是两种重要的线性结构。从数据结构来看&#xff0c;栈和队列也是线性表&#xff0c;其特殊性在于栈和队列的基本操作是线性表操作的子集&#xff08;也具有顺序结构和链式结构&#xff09;&#xff0c;它们是操作受限的…

链表,队列和栈的区别

链表&#xff0c;队列和栈都是数据结构的一种。Sartaj Sahni 在他的《数据结构、算法与应用》一书中称&#xff1a;“数据结构是数据对象&#xff0c;以及存在于该对象的实例和组成实例的数据元素之间的各种联系。这些联系可以通过定义相关的函数来给出。”他将数据对象&#x…

栈与队列的定义与区别

1、栈 首先&#xff0c;普通的线性表实现是有两个端口可以访问的&#xff0c;但是如果作为栈就要封闭一端&#xff0c;只能访问另一端。这当然不是自讨苦吃&#xff0c;栈是一种抽象数据结构&#xff0c;是对现实世界对象的模拟。比如&#xff0c;自助餐厅中的一叠盘子&#x…

java 队列和栈的区别

栈和队列的区别 &#xff08;1&#xff09;数据插入删除 栈是一种特殊的线性表&#xff0c;他只能在一段进行插入和删除操作&#xff0c;就好像是一个井一样。进行数据插入和删除就类似于井口&#xff0c;称为栈定。而井也是有底部的&#xff0c;栈无法进行插入删除操作的这一…

监督学习和无监督学习的区别(机器学习)

机器学习主要分为两类 监督学习无监督学习 两者的区别主要是是否需要人工参与数据结果的标注 监督学习&#xff1a;教计算机如何去完成预测任务&#xff08;有反馈&#xff09;&#xff0c;预先给一定数据量的输入和对应的结果即训练集&#xff0c;建模拟合&#xff0c;最后让…

简单说下有监督学习和无监督学习的区别

简单说下有监督学习和无监督学习的区别 解析&#xff1a; 有监督学习&#xff1a;对具有标记的训练样本进行学习&#xff0c;以尽可能对训练样本集外的数据进行分类预测。&#xff08;LR,SVM,BP,RF,GBDT&#xff09; 无监督学习&#xff1a;对未标记的样本进行训练学习&#xf…

一个简单的例子来理解监督学习和非监督学习及其区别

首先&#xff0c;必须理解两个基本概念&#xff1a;特征值和目标值&#xff0c;先看图例 1、特征值&#xff1a; 特征值是指数据的特征&#xff0c;对于每个样本&#xff0c;通常具有一些 "属性"&#xff08;Attribute&#xff09;或者说 ”特征“&#xff08;Featu…

监督学习、无监督学习和半监督学习区别

1、概念 1.1监督学习&#xff08;数据集有输入和输出数据&#xff09;&#xff1a;通过已有的一部分输入数据与输出数据之间的相应关系。生成一个函数&#xff0c;将输入映射到合适的输出&#xff0c;比如分类。 1.2无监督学习&#xff08;数据集中只有输入&#xff09;&…

监督、自监督、半监督、无监督学习的区别

目录 一、简易版区别 二、详细版区别 一、简易版区别 A Survey on Semi-, Self-and Unsupervised Learning for Image Classification 文中的解释&#xff1a; 监督学习&#xff08;a&#xff09;&#xff1a;给出全部样本红蓝两类的标签 半监督学习&#xff08;b&#xf…

有监督学习与无监督学习

机器学习的常用方法&#xff0c;主要分为有监督学习(supervised learning)和无监督学习(unsupervised learning)。简单的归纳就是&#xff0c;是否有监督&#xff08;supervised&#xff09;&#xff0c;就看输入数据是否有标签&#xff08;label&#xff09;。输入数据有标签&…

有监督和无监督

来自有监督vs.无监督&#xff0c;傻傻分不清楚&#xff1f; - 搜狐网 网上对于有监督和无监督差异性的文章非常多&#xff0c;本文将重点从应用的角度来阐述如何选择有监督和无监督。 对比一&#xff1a;有标签 vs. 无标签 有监督又被称为“有老师的学习”&#xff0c;无监督被…

机器学习:有监督和无监督之间有什么区别

机器学习是人工智能的一个子集&#xff0c;它通过示例和经验教会计算机执行任务&#xff0c;是研究和开发的热门领域。我们每天使用的许多应用程序都使用机器学习算法&#xff0c;包括AI助手&#xff0c;Web搜索和机器翻译。 您的社交媒体新闻提要由机器学习算法提供支持。您、…