再见:深拷贝、浅拷贝问题!

2022-12-19 0 423

再见:深拷贝、浅拷贝问题!

第一类复本在他们日常生活写标识符的时候大体上是连续性市场需求,时常碰到,或者说许多人整天无暇顾及写销售业务,忽略了一些技术细节难题和认知,有时这点除非出了难题,就不太难摸查了。

因此第一集回去剖析呵呵。

注:责任编辑已收录Github开放源码工程项目:github.com/hansonwang99/JavaCollection ,里头有详尽自修程式设计自学走线、丘托韦和面经、程式设计数据资料及系列产品技术该文等,天然资源稳步预览中…

值类别 vs 提及类别

这三个基本上概念的界定,对于深、浅复本难题的认知十分重要。

正像Java旧约《Java程式设计价值观》第一章的副标题所说,在Java中所有人都可以视作第一类,因此走进Java的世界,像字符串、类Class、隐式Enum、Integer包装袋类之类,就是众所周知的提及类别;

但是Java的词汇级此基础数据类别,譬如int这些基本上类别,操作形式时一般也是采行的值传达形式,因此有时也称它为值类别。

为了易于Nenon的讲诉和总括,他们这里先表述三个类:Student和Major,分别表示「小学生」以及「学以致用的专精」,两者是包涵亲密关系:

// 小学生的学以致用专精 public class Major { private String majorName; // 专精中文名称 private long majorId; // 专精SS // … 其它略去 … } // 小学生 public class Student { private String name; // 联系电话 private int age; // 年纪 private Major major; // 学以致用专精 // … 其它略去 … }
再见:深拷贝、浅拷贝问题!

表达式 vs 浅复本 vs 深复本

第一类表达式

表达式是日常生活程式设计过程中最常用的操作形式,最简单的比如说:

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三个第一类也是完全独立的,不受互相的干扰,深复本完成。

后 记

好了,关于「深复本」和「浅复本」这个难题这次就聊到这里吧。本以为这篇会很快写完,结果又扯出了这么多东西,不过这样一剖析、一串联,感觉还是清晰了不少。

就这样吧,下篇见。

注:责任编辑已收录Github开放源码工程项目:github.com/hansonwang99/JavaCollection,里头有详尽自修程式设计自学走线、丘托韦和面经、程式设计资料及系列产品技术该文等,天然资源稳步预览中…

每天进步一点点

慢一点才能更快

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务