1.浅复制与深复制概念
浅复制(浅克隆)
被复制对象的所有变量都含有与原来的对象相同的值, 而所有的对其他对象的引用仍然指向原来的对象换言之, 浅复制仅仅复制所考虑的对象, 而不复制它所引用的对象
深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值, 除去那些引用其他对象的变量那些引用其他对象的变量将指向被复制过的新对象, 而不再是原有的那些被引用的对象换言之, 深复制把要复制的对象所引用的对象都复制了一遍
2.Java 的 clone()方法
clone 方法将对象复制了一份并返回给调用者一般而言, clone()方法满足:
对任何的对象 x, 都有 x.clone() !=x// 克隆对象与原对象不是同一个对象
对任何的对象 x, 都有 x.clone().getClass()= =x.getClass()// 克隆对象与原对象的类型一样
如果对象 x 的 equals()方法定义恰当, 那么 x.clone().equals(x)应该成立
Java 中对象的克隆
为了获取对象的一份拷贝, 我们可以利用 Object 类的 clone()方法
在派生类中覆盖基类的 clone()方法, 并声明为 public
在派生类的 clone()方法中, 调用 super.clone()
在派生类中实现 Cloneable 接口
请看如下代码:
- public class Student implements Cloneable
- {
- String name;
- int age;
- Student(String name,int age)
- {
- this.name=name;
- this.age=age;
- }
- public Object clone()
- {
- Object o=null;
- try
- {
- o=(Student)super.clone();//Object 中的 clone()识别出你要复制的是哪一个对象
- }
- catch(CloneNotSupportedException e)
- {
- System.out.println(e.toString());
- }
- return o;
- }
- public static void main(String[] args)
- {
- Student s1=new Student("zhangsan",18);
- Student s2=(Student)s1.clone();
- s2.name="lisi";
- s2.age=20;
- // 修改学生 2 后, 不影响学生 1 的值
- System.out.println("name="+s1.name+","+"age="+s1.age);
- System.out.println("name="+s2.name+","+"age="+s2.age);
- }
- }
说明:
为什么我们在派生类中覆盖 Object 的 clone()方法时, 一定要调用 super.clone()呢? 在运行时刻, Object 中的 clone() 识别出你要复制的是哪一个对象, 然后为此对象分配空间, 并进行对象的复制, 将原始对象的内容一一复制到新对象的存储空间中
继承自 java.lang.Object 类的 clone()方法是浅复制以下代码可以证明之
- class Professor
- {
- String name;
- int age;
- Professor(String name,int age)
- {
- this.name=name;
- this.age=age;
- }
- }
- public class Student implements Cloneable
- {
- String name;// 常量对象
- int age;
- Professor p;// 学生 1 和学生 2 的引用值都是一样的
- Student(String name,int age,Professor p)
- {
- this.name=name;
- this.age=age;
- this.p=p;
- }
- public Object clone()
- {
- Student o=null;
- try
- {
- o=(Student)super.clone();
- }
- catch(CloneNotSupportedException e)
- {
- System.out.println(e.toString());
- }
- o.p=(Professor)p.clone();
- return o;
- }
- public static void main(String[] args)
- {
- Professor p=new Professor("wangwu",50);
- Student s1=new Student("zhangsan",18,p);
- Student s2=(Student)s1.clone();
- s2.p.name="lisi";
- s2.p.age=30;
- System.out.println("name="+s1.p.name+","+"age="+s1.p.age);
- System.out.println("name="+s2.p.name+","+"age="+s2.p.age);
- // 输出结果学生 1 和 2 的教授成为 lisi,age 为 30
- }
- }
那应该如何实现深层次的克隆, 即修改 s2 的教授不会影响 s1 的教授? 代码改进如下
改进使学生 1 的 Professor 不改变(深层次的克隆)
- class Professor implements Cloneable
- {
- String name;
- int age;
- Professor(String name,int age)
- {
- this.name=name;
- this.age=age;
- }
- public Object clone()
- {
- Object o=null;
- try
- {
- o=super.clone();
- }
- catch(CloneNotSupportedException e)
- {
- System.out.println(e.toString());
- }
- return o;
- }
- }
- public class Student implements Cloneable
- {
- String name;
- int age;
- Professor p;
- Student(String name,int age,Professor p)
- {
- this.name=name;
- this.age=age;
- this.p=p;
- }
- public Object clone()
- {
- Student o=null;
- try
- {
- o=(Student)super.clone();
- }
- catch(CloneNotSupportedException e)
- {
- System.out.println(e.toString());
- }
- // 对引用的对象也进行复制
- o.p=(Professor)p.clone();
- return o;
- }
- public static void main(String[] args)
- {
- Professor p=new Professor("wangwu",50);
- Student s1=new Student("zhangsan",18,p);
- Student s2=(Student)s1.clone();
- s2.p.name="lisi";
- s2.p.age=30;
- // 学生 1 的教授不 改变
- System.out.println("name="+s1.p.name+","+"age="+s1.p.age);
- System.out.println("name="+s2.p.name+","+"age="+s2.p.age);
- }
- }
3.利用串行化来做深复制 (主要是为了避免重写比较复杂对象的深复制的 clone() 方法, 也可以程序实现断点续传等等功能)
把对象写到流里的过程是串行化 (Serilization) 过程, 但是在 Java 程序师圈子里又非常形象地称为冷冻或者腌咸菜 (picking) 过程; 而把对象从流中读出来的并行化(Deserialization) 过程则叫做 解冻或者回鲜 (depicking) 过程
应当指出的是, 写在流里的是对象的一个拷贝, 而原对象仍然存在于 JVM 里面, 因此腌成咸菜的只是对象的一个拷贝, Java 咸菜还可以回鲜
在 Java 语言里深复制一个对象, 常常可以先使对象实现 Serializable 接口, 然后把对象 (实际上只是对象的一个拷贝) 写到一个流里(腌成咸菜), 再从流里读出来(把咸菜回鲜), 便可以重建对象
如下为深复制源代码
- public Object deepClone()
- {
- // 将对象写到流里
- ByteArrayOutoutStream bo=new ByteArrayOutputStream();
- ObjectOutputStream oo=new ObjectOutputStream(bo);
- oo.writeObject(this);
- // 从流里读出来
- ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
- ObjectInputStream oi=new ObjectInputStream(bi);
- return(oi.readObject());
- }
这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的, 否则, 就需要仔细考察那些不可串行化的对象或属性可否设成 transient, 从而将之排除在复制过程之外上例代码改进如下
- class Teacher implements Serializable {
- String name;
- int age;
- public void Teacher(String name, int age) {
- this.name = name;
- this.age = age;
- }
- }
- public class Student implements Serializable {
- String name; // 常量对象
- int age;
- Teacher t; // 学生 1 和学生 2 的引用值都是一样的
- public void Student(String name, int age, Teacher t) {
- this.name = name;
- this.age = age;
- this.p = p;
- }
- public Object deepClone() throws IOException,
- OptionalDataException,
- ClassNotFoundException { // 将对象写到流里
- ByteArrayOutoutStream bo = new ByteArrayOutputStream();
- ObjectOutputStream oo = new ObjectOutputStream(bo);
- oo.writeObject(this); // 从流里读出来
- ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
- ObjectInputStream oi = new ObjectInputStream(bi);
- return (oi.readObject());
- }
- public static void main(String[] args) {
- Teacher t = new Teacher("tangliang", 30);
- Student s1 = new Student("zhangsan", 18, t);
- Student s2 = (Student) s1.deepClone();
- s2.t.name = "tony";
- s2.t.age = 40;
- // 学生 1 的老师不改变
- System.out.println("name=" + s1.t.name + "," + "age=" + s1.t.age);
- }
- }
来源: http://www.codeceo.com/article/java-object-clone.html