深拷贝(deep clone)与浅拷贝(shallow clone)
一、浅复制和深复制概念
浅复制(浅克隆): 被复制对象的所有变量都含有与原来对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不是复制它所引用的对象。
深复制(深克隆): 被复制对象的所有变量都含有与原来对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象, 而不再试原有的那些被引用的对象。换言之,深复制把复制的对象所应用的对象都复制了一遍。
二、Java的Clone()方法 【定义在Object类中】
clone方法将对象复制一份并返回给调用者。
一般而言,clone() 方法满足:
1、对任何对象x,都有x.clone() != x
克隆对象与原对象不是一个对象
2、对任何的对象x,都有
x.clone().getClass() == x.getClass()
克隆对象与原对象的类型一样
3、如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立
二、浅复制Demo
public class CloneTest1 {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setAge(20);
student.setName("Larry");
Student student2 = (Student)student.clone();
System.out.println(student2.getAge());
System.out.println(student2.getName());
System.out.println("-----------------------");
student2.setName("Nick");
System.out.println(student.getName());
System.out.println(student2.getName());
}
}
class Student implements Cloneable{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
打印结果
20
zhangsan
-----------------------
zhangsan
Lisi
student2.setName("Nick"); 执行后,student2的name赋予新的值,指向新的空间。如下图

三、深复制
以下代码还是浅复制
public class CloneTest2 {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher();
teacher.setAge(40);
teacher.setName("Teacher zhang");
Student2 s1 = new Student2();
s1.setAge(20);
s1.setName("zhangsan");
s1.setTeacher(teacher);
Student2 s2 = (Student2)s1.clone();
System.out.println(s2.getAge());
System.out.println(s2.getName());
teacher.setName("Teacher Li");
System.out.println(s2.getTeacher().getAge());
System.out.println(s2.getTeacher().getName());
}
}
class Teacher {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Student2 implements Cloneable{
private int age;
private String name;
private Teacher teacher;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
修改成深复制
Teacher也实现Clone方法

然后改写Student的Clone方法

clone Student的时候,修改Teacher。
修改后的完整代码:
public class CloneTest2 {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher();
teacher.setAge(40);
teacher.setName("Teacher zhang");
Student2 s1 = new Student2();
s1.setAge(20);
s1.setName("zhangsan");
s1.setTeacher(teacher);
Student2 s2 = (Student2)s1.clone();
System.out.println(s2.getAge());
System.out.println(s2.getName());
teacher.setName("Teacher Li");
System.out.println(s2.getTeacher().getAge());
System.out.println(s2.getTeacher().getName());
}
}
class Teacher implements Cloneable{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student2 implements Cloneable{
private int age;
private String name;
private Teacher teacher;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student2 student2 = (Student2)super.clone();
student2.setTeacher((Teacher)student2.getTeacher().clone());
return student2;
}
}
五、利用序列化来做深复制(最简单也是最多的一种方式)
1、使用Clone方法实现深复制存在的缺点: 存在多个引用的成员变量,将做多次处理。

如上图做了一次处理,如果有多个应用,将做多次处理。
2、序列化/反序列化
把对象写到流里的过程是序列化(Serilization)过程,而把对象从流中读取出来的过程则叫饭序列化(Deserialization)过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面
1)在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
2)这样做的前提示对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient,从而将之排除在复制过程之外。
3)Code
public class CloneTest3 {
public static void main(String[] args) throws Exception {
Teacher3 teacher = new Teacher3();
teacher.setAge(40);
teacher.setName("Teacher zhang");
Student3 s1 = new Student3();
s1.setAge(20);
s1.setName("zhangsan");
s1.setTeacher(teacher);
Student3 s2 = (Student3)s1.deepCopy();
System.out.println(s2.getAge());
System.out.println(s2.getName());
System.out.println("-------------");
System.out.println(s2.getTeacher().getAge());
System.out.println(s2.getTeacher().getName());
s2.getTeacher().setAge(50);
s2.getTeacher().setName("Teacher Li");
System.out.println(s1.getTeacher().getAge());
System.out.println(s1.getTeacher().getName());
}
}
class Teacher3 implements Serializable{
//private static final long serialVersionUID = 675697793444541314L;
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Student3 implements Serializable{
//private static final long serialVersionUID = -7008294898863009451L;
private int age;
private String name;
private Teacher3 teacher;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher3 getTeacher() {
return teacher;
}
public void setTeacher(Teacher3 teacher) {
this.teacher = teacher;
}
public Object deepCopy() throws Exception {
//将对象写入流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//从流中读取对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
打印结果:
20
zhangsan
-------------
40
Teacher zhang
40
Teacher zhang
六、关于实现Seriallizable接口的类中的serialVersionUID问题

如果你的对象序列化后存在硬盘上面后,可是后来你更改了类的field(增加或减少或改名),当你反序列化时,就会出现Execption的,这样就会造成不兼容性的问题。
但当serialVersionUID相同时,它就会将不一样的field以type的缺省值Deserialize,这样可以避开不兼容的问题。
深拷贝(deep clone)与浅拷贝(shallow clone)的更多相关文章
- 深拷贝(deep clone)与浅拷贝(shallow clone)
深拷贝(deep clone)与浅拷贝(shallow clone) 浅复制(浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复 ...
- java的clone()、浅拷贝与深拷贝
clone()方法是Object的native方法.protected native Object clone() throws CloneNotSupportedException; 声明为pro ...
- Java:浅克隆(shallow clone)与深克隆(deep clone)
Summary 浅克隆与深克隆对于JavaSE来说,是个难度系数比较低的概念,但不应该轻视它. 假设一个场景:对于某个list,代码里并没有任何对其的直接操作,但里面的元素的属性却被改变了,这可能就涉 ...
- 由Python的浅拷贝(shallow copy)和深拷贝(deep copy)引发的思考
首先查看拷贝模块(copy)发现: >>> help(copy)Help on module copy:NAME copy - Generic (shallow and dee ...
- copy&mutableCopy 浅拷贝(shallow copy)深拷贝 (deep copy)
写在前面 其实看了这么多,总结一个结论: 拷贝的初衷的目的就是为了:修改原来的对象不能影响到拷贝出来得对象 && 修改拷贝出来的对象也不能影响到原来的对象 所以,如果原来对象就是imm ...
- shallow clone
shallow clone 浅克隆经常在一些大型仓库中很有用——不用花费大量时间去clone一个完整的仓库,仅仅checkout出来某个分支(如master)的最新N次递交: git clone -- ...
- Java中的深拷贝(深复制)和浅拷贝(浅复制)
深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在C++语言中,若不弄懂,则会在delete的时候出问题,但是我们在这幸好用的是Java.虽然java自动管理对象的回收,但对于深拷贝(深复 ...
- git clone 和 download 不一样,能用git clone 就用git clone,download的代码,经常出现安装bug
git clone 和 download 不一样,能用git clone 就用git clone,download的代码,经常出现安装bug
- 浅谈浅克隆(shallow clone)和 深克隆(deep clone)
区别就在于是否对对象中的引用变量所指向的对象进行拷贝. 1.浅克隆/浅复制/浅拷贝 浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,没有对引用指向 ...
随机推荐
- MySQL Lock--MySQL加锁学习1
准备测试数据: ## 开启InnoDB Monitor SET GLOBAL innodb_status_output=ON; SET GLOBAL innodb_status_output_lock ...
- MYSQL 存储引擎概述
一.存储引擎 Mysql中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中每一种技术都使用了不同的存储机制,索引技巧.锁定水平并且最终提供广泛的不同功能和能力.通过选择不同的技术,你能够获得 ...
- kubernetes 集群添加node节点
kubernetes 集群添加node节点 注意,我们并不需要把新增的node ip加入到证书里重新生成!!! 下面我们以添加node03为例 一.添加集群里个节点的hostname并设置好对应主机名 ...
- 学习python的日常6
错误.调试和测试: 错误处理: try: print('try...') r = 10 / 0 print('result:', r) except ZeroDivisionError as e: p ...
- SpringBoot使用MockMVC单元测试Controller
对模块进行集成测试时,希望能够通过输入URL对Controller进行测试,如果通过启动服务器,建立http client进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络 ...
- docker发展历程
docker发展历程 docker本身不是容器,它只是一个更加易用的前端管理器. 最早期的容器技术概念是用chroot来实现隔离,但是chroot只是提供了对进程文件目录虚拟化的功能,不能防止进程恶意 ...
- APC (Asynchronous Procedure Call)
系统创建新线程时,会同时创建与这个线程相关联的队列,即异步过程调用(APC)的队列. 一些异步操作可以通过加入APC来实现,比如我现在学习的IO请求/完成. BOOL ReadFileEx( HAND ...
- prometheus 告警规则
GitHub网址1 https://github.com/samber/awesome-prometheus-alerts 网址2 https://awesome-prometheus-alerts. ...
- Java 使用Builder解决构造函数参数过多的问题
原文:https://blog.csdn.net/michael_f2008/article/details/77715075 //Builder Pattern public class Nutri ...
- 用js刷剑指offer(第一个只出现一次的字符)
题目描述 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写). 牛客网链接 js代码 fu ...