对象赋值

赋值是日常编程过程中最常见的操作,最简单的比如:

Student codeSheep = new Student(); Student codePig = codeSheep;

严格来说,这种不能算是对象拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象:

浅拷贝

浅拷贝属于对象克隆方式的一种,重要的特性体现在这个 「浅」 字上。

比如我们试图通过studen1实例,拷贝得到student2,如果是浅拷贝这种方式,大致模型可以示意成如下所示的样子:

很明显,值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。

深拷贝

深拷贝相较于上面所示的浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本

浅拷贝代码实现

还以上文的例子来讲,我想通过student1拷贝得到student2,浅拷贝的典型实现方式是:让被复制对象的类实现Cloneable接口,并重写clone()方法即可。

以上面的Student类拷贝为例:

public class Student implements Cloneable { private String name; // 姓名 private int age; // 年龄 private Major major; // 所学专业 @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } // ... 其他省略 ... }

public class Test { public static void main(String[] args) throws CloneNotSupportedException { Major m = new Major("计算机科学与技术",666666); Student student1 = new Student( "CodeSheep", 18, m ); // 由 student1 拷贝得到 student2 Student student2 = (Student) student1.clone(); System.out.println( student1 == student2 ); System.out.println( student1 ); System.out.println( student2 ); System.out.println( "\n" ); // 修改student1的值类型字段 student1.setAge( 35 ); // 修改student1的引用类型字段 m.setMajorName( "电子信息工程" ); m.setMajorId( 888888 ); System.out.println( student1 ); System.out.println( student2 ); } }

运行结果:

从结果可以看出:

student1==student2打印false,说明clone()方法的确克隆出了一个新对象;

修改值类型字段并不影响克隆出来的新对象,符合预期;

而修改了student1内部的引用对象,克隆对象student2也受到了波及,说明内部还是关联在一起的

深拷贝代码实现

深度遍历式拷贝

虽然clone()方法可以完成对象的拷贝工作,但是注意:clone()方法默认是浅拷贝行为,就像上面的例子一样。若想实现深拷贝需覆写 clone()方法实现引用对象的深度遍历式拷贝,进行地毯式搜索。

所以对于上面的例子,如果想实现深拷贝,首先需要对更深一层次的引用类Major做改造,让其也实现Cloneable接口并重写clone()方法:

public class Major implements Cloneable { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } // ... 其他省略 ... }

其次我们还需要在顶层的调用类中重写clone方法,来调用引用类型字段的clone()方法实现深度拷贝,对应到本文那就是Student类:

public class Student implements Cloneable { @Override public Object clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); student.major = (Major) major.clone(); // 重要!!! return student; } // ... 其他省略 ... }

这时候上面的测试用例不变,运行可得结果:

很明显,这时候student1和student2两个对象就完全独立了,不受互相的干扰。

利用反序列化实现深拷贝

利用反序列化技术,我们也可以从一个对象深拷贝出另一个复制对象,而且在解决多层套娃式的深拷贝问题时效果出奇的好。

所以我们这里改造一下Student类,让其clone()方法通过序列化和反序列化的方式来生成一个原对象的深拷贝副本:

public class Student implements Serializable { private String name; // 姓名 private int age; // 年龄 private Major major; // 所学专业 public Student clone() { try { // 将对象本身序列化到字节流 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream( byteArrayOutputStream ); objectOutputStream.writeObject( this ); // 再将字节流通过反序列化方式得到对象副本 ObjectInputStream objectInputStream = new ObjectInputStream( new ByteArrayInputStream( byteArrayOutputStream.toByteArray() ) ); return (Student) objectInputStream.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } // ... 其他省略 ... }

当然这种情况下要求被引用的子类(比如这里的Major类)也必须是可以序列化的,即实现了Serializable接口:

public class Major implements Serializable { // ... 其他省略 ... }

这时候测试用例完全不变,直接运行,也可以得到如下结果:

很明显,这时候student1和student2两个对象也是完全独立的,不受互相的干扰,深拷贝完成。

java数组深拷贝和浅拷贝_java中的深拷贝与浅拷贝(值类型 vs 引用类型)相关推荐

  1. java中main()函数的返回值是什么_Java中main()函数的返回值类型是什么

    Java中main()函数的返回值类型是什么 答:void 在单纯形表的终表中,若非基变量的检验数有0,那么最优解 答:无穷多 中国大学MOOC: 不属于现代区位理论的是( ) 答:中心地理论 不属于 ...

  2. java 数组 去掉第一个_java中数组的理解以及如何去掉断点

    数组也是对象 数组是放置相同类型的数据 数组里面放置的每一个变量都是有下标的. 数组的长度是确定的 数组的元素类型必须是相同的 数组类型可以是任何数据类型,包括基本类型和引用类型 数组变量属于引用类型 ...

  3. java数组包含某个元素_java中判断数组是否包含某元素的方法

    有两种方法可以判断数组是否包含元素: 方法1, 将数组转换为list,然后使用list的contains方法来判断:Arrays.asList(...).contains(...) java.lang ...

  4. java 值类型与引用类型_JAVA 关于值类型和引用类型的区别

    Java中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变.值类型 ...

  5. golang中的值类型和引用类型

    值类型与引用类型 不管是Java还是golang中,都有值类型和引用类型的概念.在使用两者时,发现这两种语言之间还是有差异的. 值类型 值类型:这些类型的变量直接指向存在内存中的值,值类型的变量的值存 ...

  6. python中的引用类型_Python中的值类型与引用类型

    其实各个标准资料中没有说明Python有值类型和引用类型的分类,这个分类一般是C++和Java中的.但是语言是相通的,所以Python肯定也有类似的.实际上Python 的变量是没有类型的,这与以往看 ...

  7. 值类型和引用类型在栈和堆中的分配

      类型基础及背后的工作原理   数据在内存中的分配与传递    值类型和引用类型它们在内存分配与传递上的区别 内存分配 首先要了解一下内存中栈和堆的概念.     栈(Stack) ##栈是一种先进 ...

  8. java 数组声明并初始化_Java数组的声明与初始化

    在<Java数组简介>一节中,我们已经了解了什么是数组.什么是基本数据类型的数组,那么大家是不是迫不及待地想知道一个基本数据类型的数组是什么样子的呢?下面我们就一起来揭开它神秘的面纱吧! ...

  9. java判断重复字符个数_Java中判断字符串中相同字符的个数

    译文---C#堆VS栈(Part Three) 前言 在本系列的第一篇文章中,介绍了值类型和引用类型在参数传递时的不同,本文将讨论如何应用ICloneable接口实现去修复引在堆上的用变量所带来的问题 ...

最新文章

  1. 利用ZYNQ SOC快速打开算法验证通路(5)——system generator算法IP导入IP integrator
  2. PIE SDK Geometry的坐标转换
  3. bzoj 2109 amp; 2535 空中管制 解读
  4. ipvsadm使用和命令
  5. RHEL在VM虚拟机下仅主机模式不能联网的解决方法
  6. 【Prometheus】prometheus生成快照,并冷备份数据,恢复数据
  7. 《骑马与砍杀》:CRPG精神的另类复活
  8. NetBeans Weekly News 刊号 # 27 - Sep 24, 2008
  9. 数据结构:排序算法之堆排序和选择排序
  10. 【链接】Solr的Filed中indexed与stored属性
  11. twisted 安装时,安装顺序为 zope.interface -twisted
  12. IndexNotReadyException: Please change caller according to com.intellij.openapi.project.IndexNotReady
  13. 行业巨头争相布局物联网
  14. 电商产品经理必修课之学员招募及在线课程学习
  15. java 事务嵌套_Java事务以及嵌套事务
  16. 要命啦!Word中快速录入大全,内含快捷键小技巧,快来一起学习!
  17. 测试环境部署——selenium+python
  18. php rsa模拟登录微博,微博模拟登录爬虫
  19. 网站关键词优化的五点原则
  20. ui设计线上培训怎么样?ui设计线上与线下的区别?

热门文章

  1. asp.netcore3.0 使用 DbProviderFactories 连接数据库
  2. ASP.NET Core SameSite 设置引起 Cookie 在 QQ 浏览器中不起作用
  3. SuperSocket 2.0 Preview1 发布,.NET Socket服务器框架
  4. .NET Core 如何禁止.resx文件自动生成Designer.cs
  5. 实现自己的.NET Core配置Provider之Yaml
  6. TypeScript 2.0 正式发布
  7. django17:importlib应用中间件代码思想
  8. powershell 查看系统设备\device status
  9. 面试中get和post的区别
  10. Linux中的vi和vim