(参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201#reply)
(序列化参考:http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html)
至于克隆与复制的区别,文章已经说明的很清楚了
关于“=”号复制问题
/** * Project Name:DesignPatterns * File Name:Employee.java * Package Name:com.louis.seriesClone * Date:2017年9月28日下午8:04:28 * Copyright (c) 2017, 2692613726@qq.com All Rights Reserved. * */ package com.louis.seriesClone; /** * ClassName:Employee * Function: 关于“=”号复制问题 * Reason: TODO ADD REASON. * Date: 2017年9月28日 下午8:04:28 * @author michael * @version * @since JDK 1.7 * @see */ public class Employee { public Employee(){ } public Employee(String name, int age){ this.age = age; this.name = name; } 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 static void main(String[] args){ Employee demo1 = new Employee("rollen", 20); Employee demo2 = demo1; demo2.setAge(100); demo2.setName("hello world"); System.out.println(demo1); System.out.println(demo2); System.out.println(demo1.getName()); System.out.println(demo2.getName()); } private String name; private int age; }
浅拷贝和深拷贝
由于age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。但是name是String类型的, 它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。
注意下面注释的部分:是否注释造成结果不一样,判断是浅克隆还是深克隆应该注释掉该部分。第二种结果要注意String类型的变量clone后的表现好象也实现了深度clone,但其实只是一个假象。
/** * Project Name:DesignPatterns * File Name:Employee.java * Package Name:com.louis.seriesClone * Date:2017年9月28日下午8:04:28 * Copyright (c) 2017, 2692613726@qq.com All Rights Reserved. * */ package com.louis.seriesClone; /** * ClassName:Employee * Function: TODO ADD FUNCTION. * Reason: TODO ADD REASON. * Date: 2017年9月28日 下午8:04:28 * @author michael * @version * @since JDK 1.7 * @see */ public class Employee1 implements Cloneable{ public Employee1(){ } private String name; private int age; public Employee1(String name, int age){ this.age = age; this.name = name; } @Override public String toString(){ return "姓名: " + name + "年龄: " + 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; } protected Object clone() throws CloneNotSupportedException { return (Employee1)super.clone(); } public static void main(String[] args) { Employee1 demo1 = new Employee1("rollen", 20); Employee1 demo2; try { demo2 = (Employee1) demo1.clone(); /* demo2.setAge(100); demo2.setName("hello world"); System.out.println(demo1); System.out.println(demo2);*/ String result = demo1.getName() == demo2.getName() ? "clone是浅拷贝的" : "clone是深拷贝的"; System.out.println(result); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
Cloneable实现深克隆
现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的
static class Body implements Cloneable{ public Head head; public Body() {} public Body(Head head) {this.head = head;} @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } static class Head /*implements Cloneable*/{ public Face face; public Head() {} public Head(Face face){this.face = face;} } public static void main(String[] args) throws CloneNotSupportedException { Body body = new Body(new Head()); Body body1 = (Body) body.clone(); System.out.println("body == body1 : " + (body == body1) ); System.out.println("body.head == body1.head : " + (body.head == body1.head)); }
static class Body implements Cloneable{ public Head head; public Body() {} public Body(Head head) {this.head = head;} @Override protected Object clone() throws CloneNotSupportedException { Body newBody = (Body) super.clone(); newBody.head = (Head) head.clone(); return newBody; } } static class Head implements Cloneable{ public Face face; public Head() {} public Head(Face face){this.face = face;} @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public static void main(String[] args) throws CloneNotSupportedException { Body body = new Body(new Head()); Body body1 = (Body) body.clone(); System.out.println("body == body1 : " + (body == body1) ); System.out.println("body.head == body1.head : " + (body.head == body1.head)); }
Serializable实现深克隆
把对象写到流里的过程是序列化过程(Serialization),而把对象从流中读出来的过程则叫做反序列化过程(Deserialization)。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于
JVM里面。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将其排除在复制过程之外。
注意Cloneable与Serializable接口都是marker Interface,也就是说它们只是标识接口,没有定义任何方法。
/** * Project Name:DesignPatterns * File Name:Teacher3.java * Package Name:com.louis.seriesClone * Date:2017年9月29日上午7:58:21 * Copyright (c) 2017, 2692613726@qq.com All Rights Reserved. * */ package com.louis.seriesClone; import java.io.Serializable; import org.omg.CORBA.PRIVATE_MEMBER; /** * ClassName:Teacher3 * Function: TODO ADD FUNCTION. * Reason: TODO ADD REASON. * Date: 2017年9月29日 上午7:58:21 * @author michael * @version * @since JDK 1.7 * @see */ public class Teacher3 implements Serializable{ 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; } }
/** * Project Name:DesignPatterns * File Name:student3.java * Package Name:com.louis.seriesClone * Date:2017年9月29日上午7:59:28 * Copyright (c) 2017, 2692613726@qq.com All Rights Reserved. * */ package com.louis.seriesClone; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * ClassName:student3 * Function: TODO ADD FUNCTION. * Reason: TODO ADD REASON. * Date: 2017年9月29日 上午7:59:28 * @author michael * @version * @since JDK 1.7 * @see */ public class Student3 implements Serializable{ private String name; private int age; private Teacher3 teacher3; 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 Teacher3 getTeacher3() { return teacher3; } public void setTeacher3(Teacher3 teacher3) { this.teacher3 = teacher3; } //序列化实现克隆 public Object deepClone() 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(); } }
/** * Project Name:DesignPatterns * File Name:CloneTest3.java * Package Name:com.louis.seriesClone * Date:2017年9月29日上午8:06:41 * Copyright (c) 2017, 2692613726@qq.com All Rights Reserved. * */ package com.louis.seriesClone; /** * ClassName:CloneTest3 * Function: TODO ADD FUNCTION. * Reason: TODO ADD REASON. * Date: 2017年9月29日 上午8:06:41 * @author michael * @version * @since JDK 1.7 * @see */ public class CloneTest3 { public static void main(String[] args) throws Exception { Teacher3 t = new Teacher3(); t.setAge(50); t.setName("Teacher Wang"); Student3 s1 = new Student3(); s1.setAge(20); s1.setName("ZhangSan"); s1.setTeacher3(t); Student3 s2 = (Student3) s1.deepClone(); System.out.println("拷贝得到的信息:"); System.out.println(s2.getName()); System.out.println(s2.getAge()); System.out.println(s2.getTeacher3().getName()); System.out.println(s2.getTeacher3().getAge()); System.out.println("---------------------------"); // 将复制后的对象的老师信息修改一下: s2.getTeacher3().setName("New Teacher Wang"); s2.getTeacher3().setAge(28); System.out.println("修改了拷贝对象的教师后:"); System.out.println("拷贝对象的教师:"); System.out.println(s2.getTeacher3().getName()); System.out.println(s2.getTeacher3().getAge()); System.out.println("原来对象的教师:"); System.out.println(s1.getTeacher3().getName()); System.out.println(s1.getTeacher3().getAge()); } }
serialVersionUID问题
当一个类实现了Serializable接口时,表明该类可被序列化,这个时候Eclipse会给出一个警告,要求你为该类定义一个字段,该字段名字为serialVersionUID,类型为long,提示信息如下: The serializable class Teacher3 does not declare a static final serialVersionUID field of type long。 在Eclipse中有两种生成方式: 一个是默认的1L; private static final long serialVersionUID = 1L; 一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如: private static final long serialVersionUID = -932183802511122207L; 如果你没有考虑到兼容性的问题,就把它关掉,不过有这个功能是好的,只要任何类别实现了Serializable接口,如果没有加入serialVersionUID,Eclipse都会给你提示,这个serialVersionUID为了让该类别Serializable向后兼容。 如果你的对象序列化后存到硬盘上面后,你却更改了类的field(增加或减少或改名),当你反序列化时,就会出现异常,这样就会造成不兼容性的问题。 但当serialVersionUID相同时,它就会将不一样的field以type的缺省值Deserialize,这个可以避开不兼容性的问题。