引用拷贝,浅拷贝,深拷贝

news/2024/9/28 21:16:34

参考资料

水平有限,欢迎交流!
kimi
【【每天一个技术点】引用拷贝、浅拷贝、深拷贝】
一文搞懂Java引用拷贝、浅拷贝、深拷贝 - bigsai - 博客园 (cnblogs.com)
【黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难】

1. 引用拷贝 (起绰号)

引用拷贝并不是真正意义上的拷贝,而是创建了一个新的指针指向已存在的数据。在这种拷贝方式下,原始数据和拷贝数据在内存中占用相同的位置。

特点:

  • 不创建新的对象,只是创建了一个新的引用(指针)指向已存在的对象。
  • 修改其中一个对象会影响另一个对象,因为他们指向同一个内存地址。

2. 浅拷贝(只拷贝基本数据类型,引用类型未在堆区开辟内存)

浅拷贝会创建一个新的对象,其字段值与原始对象相同。但是,如果原始对象的字段是引用类型,则拷贝的是引用,而不是实际的对象。

特点:

  • 创建一个新的对象,并将原始对象的值复制到新对象中。
  • 如果字段是基本类型,拷贝的是值。
  • 如果字段是引用类型,拷贝的是引用,而不是引用的对象。

3. 深拷贝(完全不同的对象,但值相同,在堆区开辟内存)

深拷贝会创建一个新的对象,并且递归地复制所有字段和它们的值。如果字段是对象,那么会为这个对象也创建一个拷贝。

特点:

  • 创建一个新的对象,并且递归地复制所有字段和它们的值。
  • 无论字段是基本类型还是引用类型,都会创建一个新的副本。

4.引用拷贝和浅拷贝在操纵引用类型变量(与原型引用变量的耦合关系)可能引发的问题

由于引用拷贝和浅拷贝在拷贝引用类型变量时仅仅是拷贝了其引用,而未在堆区申请内存,因此与原型变量存在耦合关系,改变一方,二者一起变化

  1. 修改影响: 如果通过一个引用修改了对象的状态,那么通过另一个引用观察到的对象状态也会发生改变,因为它们指向同一个对象。
  2. 并非独立: 程序员可能误以为两个引用是独立的,从而在代码中引入错误。
  3. 内存泄漏: 如果不小心,可能会导致内存泄漏,因为两个引用都指向同一个对象,导致对象无法被垃圾回收。
  4. 共享内部对象: 对于引用类型的字段,原始对象和浅拷贝对象仍然共享相同的内部对象,修改其中一个对象的引用类型字段会影响到另一个。

5. 案例

定义 Address 类

public class Address {  private String street;  public Address(String street) {  this.street = street;  }  public String getStreet() {  return street;  }  public void setStreet(String street) {  this.street = street;  }  
}

定义 Person 类

public class Person implements Cloneable{  private String name;  private Address address;  public Person(String name, Address address) {  this.name = name;  this.address = address;  }  public String getName() {  return name;  }  public void setName(String name) {  this.name = name;  }  public Address getAddress() {  return address;  }  public void setAddress(Address address) {  this.address = address;  }  @Override  public Object clone() {  //浅拷贝  try {  return super.clone();  } catch (CloneNotSupportedException e) {  throw new AssertionError();  }  }  public Object deepClone() {  //深拷贝  try {  Person clonedPerson = (Person) super.clone();  clonedPerson.setAddress(new Address(this.getAddress().getStreet()));  return clonedPerson;  } catch (CloneNotSupportedException e) {  throw new AssertionError();  }  }  
}

定义主类

public class Main {  static Person original = new Person("Alice", new Address("123 Main St"));  public static void referenceCopy(){  // 引用拷贝  System.out.println("引用拷贝");  Person referenceCopy = original;  System.out.println(original==referenceCopy);  System.out.println(original.getAddress()==referenceCopy.getAddress());  }  public static void shallowCopy(){  // 浅拷贝  System.out.println("浅拷贝");  Person shallowCopy = null;  shallowCopy = (Person) original.clone();  System.out.println(original==shallowCopy);  System.out.println(original.getAddress()==shallowCopy.getAddress());  }  public static void deepCopy(){  // 深拷贝  System.out.println("深拷贝");  Person deepCopy = null;  deepCopy = (Person) original.deepClone();  System.out.println(original==deepCopy);  System.out.println(original.getAddress()==deepCopy.getAddress());  }  public static void main(String[] args) {  referenceCopy();  shallowCopy();  deepCopy();  }  
}

结果分析

运行结果

引用拷贝

original与referenceCopy: true

这意味着 referenceCopy 实际上是指向 original 的同一个对象。也就是说,referenceCopyoriginal 是同一个 Person 对象的不同引用,因此它们的地址是相同的。

original.getAddress()与referenceCopy.getAddress(): true

这表明 referenceCopyoriginal 共享同一个 Address 对象。因此,对其中一个对象的 Address 属性所做的任何更改都会反映到另一个对象上。

浅拷贝

original与shallowCopy: false

这意味着 shallowCopyoriginal 是两个不同的对象,即它们在内存中的地址不同。

original.getAddress()与shallowCopy.getAddress(): true

这表明虽然 shallowCopyoriginal 是不同的 Person 对象,但它们的 Address 属性引用的是同一个 Address 对象。因此,如果修改了 shallowCopyAddress 属性,那么 originalAddress 属性也会受到影响。

深拷贝

original与deepCopy: false

这表明 deepCopyoriginal 是两个不同的 Person 对象。

original.getAddress()与deepCopy.getAddress(): false

这表明 deepCopyoriginal 不仅是不同的 Person 对象,而且它们的 Address 属性也分别指向不同的 Address 对象。因此,对 deepCopyAddress 属性所做的任何更改都不会影响到 originalAddress 属性。

总结

  • 引用拷贝 创建了指向同一对象的新引用,所以任何对这个对象的更改都会影响到所有引用该对象的变量。
  • 浅拷贝 创建了一个新的顶层对象,但是其中的引用属性仍然指向原有的对象,所以对引用属性的更改会影响所有共享该引用的对象。
  • 深拷贝 创建了一个完全独立的新对象,包括所有嵌套的对象,因此对新对象的任何更改都不会影响到原对象。

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

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

相关文章

黑马PM-内容管理-运营管理

消息推送账号管理权限管理日志管理

Blender快速入门教程1简介

0 简介Blender是最著名的 3D 计算机图形制作免费程序之一。有了 Blender,你可以创建角色、道具、环境以及你的想象力所能产生的几乎所有其他东西。它不仅可以创建对象。你还可以让它们运动起来。在动画中讲述一个故事,带领人们穿越你自己创造的世界,或者为一些视频片段添加特…

今天我有博客了!

今天我有博客了!我要把我的编程心得写下来。 今天,我先写了蜗牛打怪兽。我发现需要打的次数都是2^(n)-1,于是我用while循环求出是2的几次方,再用变量一直乘2,最后-1。以下是具体示例。接着,我写了POW。这题限制比较紧,所以不能直接算,得看c是奇数还是偶数:偶数比绝对值…

『模拟赛』csp-s模拟赛6

『模拟赛』csp-s模拟赛6『模拟赛』csp-s模拟赛6 挂分日寄:0+20+0+0 喵喵赛时对拍拍了10000个点都没拍出来,赛后一下就拍出错来了,我谔谔。 T1 DP喵~ 首先 sort 一遍方便处理 其实转移时加一个 abs 取绝对值就可,纯纯多此一举 设 \(f[i,j,1/0]\) 为前 \(i\) 个数中选 \(j\) …

树莓派pico rp2040 使用rust 在ssd1306上显示中文信息

使用u8g2-font + embedded-graphics ,在rp2040 pico上用 ssd1306输出中文信息在rp2040上用DHT22 + ssd1306显示温度信息,用 embedded-graphics库和ssd1306库来实现。但实现的效果不是很理想,无法在ssd1306屏幕上显示中文。为了解决这个问题,在github和crates.io上面找了几天…

typeScript 的第一步---安装

Node.js/浏览器,只认识JS代码,不认识TS代码,需要将TS代码转化为JS代码,然后才能运行。 安装命令:npm i -g typescript 或者 yarn global add typescript 注意:Mac电脑安装全局包时,需要通知添加sudo获取权限。sudo npm i -g typescript 验证安装是否成功:tsc -v 查…

为什么用 AWS CLI?因为我懒得点鼠标!

在这篇博客中,我们一起深入探索 AWS CLI 的世界,从零开始,逐步构建在云端的家园。将介绍 AWS CLI 的基本功能和使用场景,如何创建 IAM 用户、VPC、子网、安全组、EC2 实例等,甚至还会搭建一个应用负载均衡器(ALB)。无论你是初学者还是有一定基础的用户,都能通过本指南掌…

妙用编辑器:使用Notepad--正则表达式从命令结果报文快速生成新命令

应用场景 日常生活中有些维护场景,比如检查设备状态,执行查询命令后,得到精简结果报文,如果要更深入的检查状态,可能还要执行其他命令,逐个对象进行查询,这里涉及到快速从报文生成查询指令的功能。 比如有如下一个从LST 命令查询出来的报文,需要快速的生成DSP命令,逐个…