深拷贝(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】Explain笔记
Explain -- 使用 Explain + SQL 分析执行计划: id:表示此表的执行优先级 id相同,表的执行顺序依次从上往下: id不同,并且递增,id值越大执 ...
- 过滤器实现Token验证(登录验证+过期验证)---简单的实现
功能:登录验证+过期验证+注销清除cookie+未注销下关闭或刷新浏览器仍可直接访问action概述:token只存在客户端cookie,后端AES加密+解密+验证,每一次成功访问action都会刷新 ...
- Spring Boot+STOMP解决消息乱序问题
当我们使用Spring Boot+websocket进行前后端进行通信时,我们需要注意:服务器可以随时向客户端发送消息.默认的情况下,不保证:服务器发送的消息与到达客户端的消息的顺序是一致的.可能先发 ...
- Codeforces Round #588 (Div. 2)-E. Kamil and Making a Stream-求树上同一直径上两两节点之间gcd的和
Codeforces Round #588 (Div. 2)-E. Kamil and Making a Stream-求树上同一直径上两两节点之间gcd的和 [Problem Description ...
- Angular CLI behind the scenes, part one
原文:https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art074 --------------------------- ...
- 一.什么是protobuf
Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 ...
- 在linux系统中安装MySQL
1.安装Linux系统中自带的MySQL安装包 在现在常用的发行版本里都集中了MySQL安装包 CentOS系统中的YUM中包含了MySQL安装包,版本是MySQL5,rpm软件包的名称是mysql- ...
- python是什么编程语言。
python是一门动态解释性的强类型定义语言.
- kafka的HA机制
1.kafka的HA机制:副本机制+消息同步+leader选举. 每个topic有多个partition,每个partition又有多个副本,这些副本当中也存在不同的角色,leader.followe ...
- 运算符 & | ^ ~ >> << 讲解
字节”是byte,“位”是bit :1 byte = 8 bit : char 在java中是2个字节.java采用unicode,2个字节(16位)来表示一个字符. char 16位2个字节 byt ...