深克隆与浅克隆

1)深克隆与浅克隆概念

首先在此做一点声明,本文所说的克隆和有的地方所说的拷贝及复制是一个概念,我比较喜欢叫克隆,下文当中全部叫“克隆”。

浅克隆

被克隆对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅克隆仅仅克隆所考虑的对象,而不克隆它所引用的对象。

 

深克隆

被克隆对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被克隆过的新对象,而不再是原有的那些被引用的对象。换言之,深克隆把要克隆的对象所引用的对象都克隆了一遍。

 

如果你对这个概念不太清楚,没关系可以直接看下文的代码及图例

2Javaclone()方法

clone方法将对象克隆了一份并返回给调用者。一般而言,clone()方法满足:

对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象

对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样

如果对象xequals()方法定义恰当,那么x.clone().equals(x)应该成立。

 

Java中对象的克隆

为了获取对象的一份克隆,我们可以利用Object类的clone()方法。

在子类中覆盖基类的clone()方法,并声明为public

在子类的clone()方法中,调用super.clone()

在子类中实现Cloneable接口。

 

请看如下代码:

 

public class Student implements java.lang.Cloneable{

private String name;

private int age;

public Student(String name,int age){

this.name=name;

this.age=age;

}

//set get 方法

public void clone()throws CloneNotSupportedException{

super.clone();

}

}

public class Test{

public static void main(String[] args)throws

CloneNotsupportedException{

Student s1=new Student("zhangsan",30);

//直接用等号赋值,会发现指向的是同一个对象,所以s2age变了之后,s1

age 也发生了改变。

Student s2=s1;

s2.setAge(20);

Student s3=null;

System.out.println(s1.getAge());

//注意在这里是不能直接调用objectclone 方法的

//s3=s1.clone();会报错,因为cloneprotected 修饰的

//Student 类中添加了clone 方法时候可以调用了

s3=(Student)s1.clone();

//需要让Student 类实现java.lang.Cloneable 接口,要注意的是cloneable

//口是标记接口,它只是标记该类对象可以被克隆,标记接口中没有方法。

//否则会抛出CloneNotSupportedException 异常

s3.setAge(40);

//注意观察下一句s1age 有没有发生变化

System.out.println(s1.getAge());

}

}

 

说明:继承自java.lang.Object类的clone()方法是浅克隆。以下代码可以证明。

 

class Teacher {

       String name;

       int age;

 

       Teacher(String name, int age) {

              this.name = name;

              this.age = age;

       }

}

 

class Student implements Cloneable {

       String name;

       int age;

       Teacher t;// 学生1和学生2的引用值都是一样的。

 

       Student(String name, int age, Teacher t) {

              this.name = name;

              this.age = age;

              this.t = t;

       }

 

       public Object clone() {

              Student stu = null;

              try {

                     stu = (Student) super.clone();

              } catch (CloneNotSupportedException e) {

                     e.printStackTrace();

              }

              return stu;

       }

 

       public static void main(String[] args) {

              Teacher t = new Teacher("lisi", 30);

              Student s1 = new Student("zhangsan", 18, t);

              Student s2 = (Student) s1.clone();

              s2.t.name = "wangwu";

              s2.t.age = 40;

              System.out.println("name=" + s1.t.name + "," + "age=" + s1.t.age);

              // 学生1的老师namewangwu,age40

       }

}

  

那应该如何实现深层次的克隆,即修改s2的老师不会影响s1的老师?代码改进如下。

class Teacher implements Cloneable {

       String name;

       int age;

 

       Teacher(String name, int age) {

              this.name = name;

              this.age = age;

       }

 

       public Object clone() {

              Object obj = null;

              try {

                     obj = super.clone();

              } catch (CloneNotSupportedException e) {

                     e.printStackTrace();

              }

              return obj;

       }

}

 

class Student implements Cloneable {

       String name;

       int age;

       Teacher t;

 

       Student(String name, int age, Teacher t) {

              this.name = name;

              this.age = age;

              this.t = t;

       }

 

       public Object clone() {

              Student stu = null;

              try {

                     stu = (Student) super.clone();

              } catch (CloneNotSupportedException e) {

                     e.printStackTrace();

              }

              stu.t = (Teacher) t.clone();

              return stu;

       }

 

       public static void main(String[] args) {

              Teacher t = new Teacher("lisi", 30);

              Student s1 = new Student("zhangsan", 18, t);

              Student s2 = (Student) s1.clone();

              s2.t.name = "wangwu";

              s2.t.age = 40;

              System.out.println("name=" + s1.t.name + "," + "age=" + s1.t.age);

              // 学生1的老师不改变。

       }

}

 

下面我们通过一个简单的图例来说明深克隆与浅克隆是怎么一回事

         深克隆与浅克隆

如果像上面这样来实现有没有问题,大家可以思考一下,在业界都是用对象序列化来实现深克隆,下面我们就来看一下。

3)利用对象序列化来实现深克隆

    把对象写到流里的过程是序列化(Serilization)过程,Java程序员又非常形象地称为冷冻或者腌咸菜(picking过程;而把对象从流中读出来的反序列化(Deserialization)过程则叫做解冻或者回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个克隆,而原对象仍然存在于JVM里面,因此腌成咸菜的只是对象的一个克隆Java咸菜还可以回鲜。

    Java语言里深克隆一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个克隆)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。

下面为深克隆源代码。

这样做的前提是对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成transient,从而将之排除在克隆过程之外。上例代码改进如下。

 

import java.io.*;

 

class Teacher implements Serializable {

       String name;

       int age;

 

       Teacher(String name, int age) {

              this.name = name;

              this.age = age;

       }

}

 

class Student implements Serializable {

       String name;// 常量对象。

       int age;

       Teacher t;// 学生1和学生2的引用值都是一样的。

 

       Student(String name, int age, Teacher t) {

              this.name = name;

              this.age = age;

              this.t = t;

       }

 

       public Object deepClone() throws IOException, OptionalDataException,

                     ClassNotFoundException {

              // 将对象写到流里

              ByteArrayOutputStream 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("lisi", 30);

              Student s1 = new Student("zhangsan", 18, t);

              Student s2 = null;

              try {

                     s2 = (Student) s1.deepClone();

              } catch (OptionalDataException e) {

                     e.printStackTrace();

              } catch (IOException e) {

                     e.printStackTrace();

              } catch (ClassNotFoundException e) {

                     e.printStackTrace();

              }

              s2.t.name = "wangwu";

              s2.t.age = 40;

              System.out.println("name=" + s1.t.name + "," + "age=" + s1.t.age);

              // 学生1的老师不改变。

       }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值