深拷贝(Deep Clone)与浅拷贝(Shadow Clone)
2018-01-20 20:09
429 查看
浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不在是原有的那些被引用的对象。换言之,深复制吧要复制的对象的所引用的对象都复制了一遍。
Java的clone()方法,定义在Object类中:
一般而言,clone()方法满足:
1.对于任何对象x,都有x.clone() != x (克隆对象与原对象不是同一个对象)
2.对于任何的对象x,都有x.clone().getClass() == x.getClass() (克隆对象与原对象的类型一样)
3.如果对象x 的equals()方法定义恰当,那么x.clone().equals(x)应该成立
Java中克隆对象的实现:
1.利用Object类的clone()方法获得对象的一份拷贝
2.在派生类中覆盖基类的clone方法,并声明为public(Object类中的clone方法为protected)
3.在派生类的clone()方法中,调用super.clone()
4.在派生类中实现Cloneable接口
Exception in thread “main” java.lang.CloneNotSupportedException: com.dream.clone.Stu at java.lang.Object.clone(Native Method):不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。
一些说明:
1.为什么要在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
2.继承自java.lang.Object类的clone()方法是浅复制
浅复制与深复制的实现
对于上述深复制,有个问题,如果一个对象中有多个引用,那么岂不是需要我们多次调用set方法去设置,这也是比较麻烦的。
对象的序列化和反序列化,即使里面含有引用对象,也可以写到目标中,此处我们利用这个特性,将待复制的对象先序列化,然后反序列化获得该对象的深拷贝。写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
如何实现?
使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
这样做的前提是对象以及对象内部所引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外。
代码实现:
serialVersionUID:long类型,如果实现了接口Serializable的类没有定义该字段,则出现如下警告:
The serializable class XXX does not declare a static final serialVersionUID field of type long.
这个字段,可以随便写一个,或者自动生成。
一个是默认的1L,比如:private static final long serialVersionUID = 1L;
一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
private static final long serialVersionUID = 894019672313994740L;
serialiVersionUID为了让该类别Serializable向后兼容。
如果你的对象序列化后存到硬盘上面后,可是后来更改了类的field(增加或者减少或改名),当你反序列化时,就会出现Exception,这样就会造成不兼容性的问题。但是当serialVersionUID相同时,它就会将不一样的field以type的缺省值反序列化,这个可以避开不兼容性的问题。
深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不在是原有的那些被引用的对象。换言之,深复制吧要复制的对象的所引用的对象都复制了一遍。
Java的clone()方法,定义在Object类中:
//将对象复制了一份并返回给调用者 protected Object clone() throws CloneNotSupportedException
一般而言,clone()方法满足:
1.对于任何对象x,都有x.clone() != x (克隆对象与原对象不是同一个对象)
2.对于任何的对象x,都有x.clone().getClass() == x.getClass() (克隆对象与原对象的类型一样)
3.如果对象x 的equals()方法定义恰当,那么x.clone().equals(x)应该成立
Java中克隆对象的实现:
1.利用Object类的clone()方法获得对象的一份拷贝
2.在派生类中覆盖基类的clone方法,并声明为public(Object类中的clone方法为protected)
3.在派生类的clone()方法中,调用super.clone()
4.在派生类中实现Cloneable接口
Exception in thread “main” java.lang.CloneNotSupportedException: com.dream.clone.Stu at java.lang.Object.clone(Native Method):不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。
一些说明:
1.为什么要在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
2.继承自java.lang.Object类的clone()方法是浅复制
浅复制与深复制的实现
//浅复制 public class CloneTest { public static void main(String[] args) throws CloneNotSupportedException { Student stu = new Student(); stu.setAge(12); stu.setName("zhangsan"); Student stu2 = (Student)stu.clone(); System.out.println(stu2.getAge()+","+stu2.getName()); //12 zhangsan stu.setName("lisi"); System.out.println(stu.getName()); //lisi System.out.println(stu2.getName()); //zhangsan } } /** * 需要实现Cloneable接口,与Serializable接口类似,只是标识性接口 */ class Student implements Cloneable{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Object clone() throws CloneNotSupportedException { return super.clone(); } }
/** * 深复制 */ public class CloneTest2 { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher = new Teacher(); teacher.setAge(40); teacher.setName("Teacher Wang"); Stu stu = new Stu(); stu.setAge(18); stu.setName("Stu LiSi"); stu.setTeacher(teacher); Stu stu2 = (Stu)stu.clone(); teacher.setName("Teacher Li"); System.out.println(stu2.getAge()+","+stu.getName()); //18,Stu LiSi System.out.println(stu2.getTeacher().getAge()+","+stu2.getTeacher().getName()); //40,Teacher Wang } } 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; } public Object clone() throws CloneNotSupportedException { return super.clone(); } } class Stu implements Cloneable{ private int age; private String name; private Teacher teacher; public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.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; } /** * 深复制:首先调用本对象的clone()实现浅复制,然后调用引用对象的clone()方法实现引用对象的复制,从而实现对象的深复制 * @return * @throws CloneNotSupportedException */ public Object clone() throws CloneNotSupportedException { Stu stu = (Stu)super.clone(); stu.setTeacher((Teacher) stu.getTeacher().clone()); return stu; } }
对于上述深复制,有个问题,如果一个对象中有多个引用,那么岂不是需要我们多次调用set方法去设置,这也是比较麻烦的。
对象的序列化和反序列化,即使里面含有引用对象,也可以写到目标中,此处我们利用这个特性,将待复制的对象先序列化,然后反序列化获得该对象的深拷贝。写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
如何实现?
使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
这样做的前提是对象以及对象内部所引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外。
代码实现:
public class CloneTest3 { public static void main(String[] args) throws Exception { Worker worker = new Worker(); worker.setAge(25); worker.setName("ZhangSan"); Manager manager = new Manager(); manager.setAge(50); manager.setName("Tom"); manager.setWorker(worker); Manager manager2 = (Manager)manager.deepCopy(); System.out.println(manager2.getAge()+","+manager2.getName()); System.out.println(manager2.getWorker().getAge()+","+manager2.getWorker().getName()); System.out.println("---------------------------------"); manager2.getWorker().setName("LiSi"); System.out.println(manager.getWorker().getAge()+","+manager.getWorker().getName()); } } class Worker implements Serializable{ 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 Manager implements Serializable{ private int age; private String name; private Worker worker; public Worker getWorker() { return worker; } public void setWorker(Worker worker) { this.worker = worker; } 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; } /** * 利用对象的序列化和反序列化实现对象的深拷贝 * @return * @throws Exception */ public Object deepCopy() throws Exception{ //字节数组输出流ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区 //将待序列化的对象数据暂存入该缓冲区 ByteArrayOutputStream bos = new ByteArrayOutputStream(); //创建对象输出流,是过滤流 ObjectOutputStream oos = new ObjectOutputStream(bos); //将对象写入 oos.writeObject(this); //bos.toArray()可以获得缓冲区内的数据 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } }
serialVersionUID:long类型,如果实现了接口Serializable的类没有定义该字段,则出现如下警告:
The serializable class XXX does not declare a static final serialVersionUID field of type long.
这个字段,可以随便写一个,或者自动生成。
一个是默认的1L,比如:private static final long serialVersionUID = 1L;
一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
private static final long serialVersionUID = 894019672313994740L;
serialiVersionUID为了让该类别Serializable向后兼容。
如果你的对象序列化后存到硬盘上面后,可是后来更改了类的field(增加或者减少或改名),当你反序列化时,就会出现Exception,这样就会造成不兼容性的问题。但是当serialVersionUID相同时,它就会将不一样的field以type的缺省值反序列化,这个可以避开不兼容性的问题。
相关文章推荐
- 深拷贝(deep clone)和浅拷贝(shallow copy)
- Java中深拷贝(Deep Clone)与浅拷贝(Shallow Clone)
- 冷藏 解冻 深拷贝deepclone
- Cloneable --shadow clone --deep clone
- IOS 深拷贝和浅拷贝问题 (deep copy and shadow copy)
- java实现shadow clone(浅克隆)与深克隆(deep clone)
- 深拷贝(deep clone)与浅拷贝(shallow clone)
- java浅拷贝(shallow clone)与深拷贝(deep clone)
- DeepClone与ShadowClone(downmoon)
- 【iPhone开发】深拷贝和浅拷贝 Deep copy and Shadow copy
- JavaScript深度复制(deep clone)的实现方法
- Java中的clone() 深拷贝 浅拷贝
- Java中的clone方法-深拷贝与浅拷贝
- js面试题:实现对象深度克隆(deepClone)的三种方案
- 解析python中的copy 和 deepcopy深拷贝浅拷贝!
- Java的一个高性能快速深拷贝方法。Cloneable?
- 【转】.NET深入学习笔记(4):深拷贝与浅拷贝(Deep Copy and Shallow Copy)
- java数组的四种拷贝方法的性能分析:for、clone、System.arraycopy、Arrays.copyof
- java.util.ArrayList.clone()是浅层拷贝