java的clone()技术

一:现在Clone已经不是一个新鲜词语了,伴随着“多莉”的产生这个词语确实很“火”过一阵子,在java中也有这么一个概念,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的?
1. Clone&Copy
假设现在有一个Employee对象,Employee tobby =new Employee(“CMTobby”,5000),通
常我们会有这样的赋值Employee cindyelf=tobby,这个时候只是简单了copy了一下reference,cindyelf和tobby都指向内存中同一个object,这样cindyelf或者tobby的一个操作都可能影响到对方。打个比方,如果我们通过cindyelf.raiseSalary()方法改变了salary域的值,那么tobby通过getSalary()方法得到的就是修改之后的salary域的值,显然这不是我们愿意看到的。我们希望得到tobby的一个精确拷贝,同时两者互不影响,这时候我们就可以使用Clone来满足我们的需求。Employee cindy=tobby.clone(),这时会生成一个新的Employee对象,并且和tobby具有相同的属性值和方法。
2. Shallow Clone&Deep Clone
Clone是如何完成的呢?Object在对某个对象实施Clone时对其是一无所知的,它仅仅
是简单地执行域对域的copy,这就是Shallow Clone。这样,问题就来了咯,以Employee为例,它里面有一个域hireDay不是基本型别的变量,而是一个reference变量,经过Clone之后就会产生一个新的Date型别的reference,它和原始对象中对应的域指向同一个Date对象,这样克隆类就和原始类共享了一部分信息,而这样显然是不利的.
这个时候我们就需要进行deep Clone了,对那些非基本型别的域进行特殊的处理,例如本例中的hireDay。我们可以重新定义Clone方法,对hireDay做特殊处理,如下代码所示:
class Employee implements Cloneable
{
        public Object clone() throws CloneNotSupportedException
        {
         Employee cloned = (Employee) super.clone();
      cloned.hireDay = (Date) hireDay.clone()
      return cloned;
        }
}
3. Clone()方法的保护机制
在Object中Clone()是被申明为protected的,这样做是有一定的道理的,以Employee
类为例,通过申明为protected,就可以保证只有Employee类里面才能“克隆”Employee对象,原理可以参考我前面关于public、protected、private的学习笔记。
4. Clone()方法的使用
Clone()方法的使用比较简单,注意如下几点即可:
a. 什么时候使用shallow Clone,什么时候使用deep Clone,这个主要看具体对象的域是什么性质的,基本型别还是reference variable
b. 调用Clone()方法的对象所属的类(Class)必须implements Clonable接口,否则在调用Clone方法的时候会抛出CloneNotSupportedException。

二:有时我们确实需要clone一个对象,最好的方法还是使用Object的clone方法,由jdk调用native方法来实现,这样效率比较高。先来个shallow clone,再来deep clone。
 
1:shallow clone
 
代码如下:
待clone的对象
package practisejava.others.clone;
/**
 * 待clone的对象,必须实现Cloneable这个tag interface
 */
public class ObjectToClone implements Cloneable{
   private int i=0;
 
   public void setNum(int i){
       this.i=i;
   }
 
   public int getNum(){
       return this.i;
   }
  
   public void tellNum(){
       System.out.println("the number is "+i);
   }
   // 重写Object的clone
   public Object clone() throws CloneNotSupportedException {
        return (ObjectToClone)super.clone();
    }
}
clone测试
package practisejava.others.clone;
/**
 * 测试clone是否成功
 */
public class CloneTest {
    public static void main(String[] args) {
        ObjectToClone otc = new ObjectToClone();
        otc.setNum(888);
      
        // 开始clone了!
        ObjectToClone cloneObject = null;
        try {
            cloneObject = (ObjectToClone) otc.clone();
        } catch (CloneNotSupportedException ex) {
            System.out.println("Sorry,Clone Not Supported!");
        }
      
        // 测试clone是否成功
        if(cloneObject!=null){
            System.out.println("before clone");
            System.out.println("ObjectToClone get number : " + otc.getNum()+"\n");
      
            System.out.println("after clone");
            System.out.println("ObjectToClone get number : " + otc.getNum());
          
            cloneObject.setNum(999);
            System.out.println("CloneObject get number : " + cloneObject.getNum());
        }      
    }
}
执行结果
before clone
ObjectToClone get number : 888
after clone
ObjectToClone get number : 888
CloneObject get number : 999
 
从运行结果看,修改了clone体的值并没有影响到原来被clone体的值,哈哈,clone成功。高兴得太早了,ObjectToClone里只有简单的非引用属性,如果有引用的属性,还能成吗?答案是否定的!看下面的例子:
被clone对象引用的对象
package practisejava.others.shallowclone;
public class ReferencedObject {
    private int i = 0;
    public ReferencedObject(int i) {
        this.i = i;
    }
    public void doubleValue(){
        this.i = 2*this.i;
    }
  
    public int getNumber(){
        return this.i;
    }
}
 
待clone对象
package practisejava.others.shallowclone;
/**
 * 必须实现Cloneable接口,否则调用函数的clone方法时会报CloneNotSupportedException错
 */
public class ObjectForShallowClone  implements Cloneable{
    private int i = 0;
    private ReferencedObject rf = null;
  
    public void setNum(int i) {
        this.i = i;
    }
  
    public void setReferencedObject(ReferencedObject rf){
        this.rf = rf;
    }
      
    public int getNumber(){
        return this.i;
    }
  
    public ReferencedObject getReferencedObject(){
        return this.rf;
    }
  
    // 重写Object的clone
    public Object clone() throws CloneNotSupportedException {
         return (ObjectForShallowClone)super.clone();
     }
}  
clone测试
package practisejava.others.shallowclone;
/**
 * clone测试
 */
public class CloneTest  {
    public static void main(String[] args) {
        ObjectForShallowClone ofsc = new ObjectForShallowClone();
        ofsc.setNum(888);
        ofsc.setReferencedObject(new ReferencedObject(1));
        // 开始clone了!
        ObjectForShallowClone deepCloneObject = null;
        try {
            deepCloneObject = (ObjectForShallowClone) ofsc.clone();
        } catch (CloneNotSupportedException ex) {
            System.out.println("Sorry,Clone Not Supported!");
        }
        // 测试clone是否成功
        if(deepCloneObject!=null){
            System.out.println("before clone");
            System.out.println("ObjectForShallowClone get number : " + ofsc.getNumber());
            System.out.println("ObjectForShallowClone ReferencedObject get number : "+ofsc.getReferencedObject().getNumber()+"\n");
          
            deepCloneObject.setNum(999);
            deepCloneObject.getReferencedObject().doubleValue();
            System.out.println("after clone");
            System.out.println("ObjectForShallowClone get number : " + ofsc.getNumber());
            System.out.println("ObjectForShallowClone ReferencedObject get number : " + ofsc.getReferencedObject().getNumber());
            System.out.println("CloneObject get number : " + deepCloneObject.getNumber());
            System.out.println("CloneObject ReferencedObject get number : " + deepCloneObject.getReferencedObject().getNumber());
        }      
    }
}
运行结果如下:
before clone
ObjectForShallowClone get number : 888
ObjectForShallowClone ReferencedObject get number : 1
after clone
ObjectForShallowClone get number : 888
ObjectForShallowClone ReferencedObject get number : 2
CloneObject get number : 999
CloneObject ReferencedObject get number : 2
 
显然,修改了clone对象的引用对象的值后,被clone对象引用对象的值也随之改变。这不是我们想要的效果,改进的方法就是deep clone。

2:deep clone
被clone对象引用的对象(比shallow clone多加了clone的方法)
package practisejava.others.deepclone;
public class ReferencedObject implements Cloneable{
    private int i = 0;
    public ReferencedObject(int i) {
        this.i = i;
    }
    public void doubleValue(){
        this.i = 2*this.i;
    }
    public int getNumber(){
        return this.i;
    }
    // 重写clone的方法
    public Object clone() throws CloneNotSupportedException {
        return (ReferencedObject)super.clone();
    }
}
待clone对象(在clone方法里同时clone引用对象)
package practisejava.others.deepclone;
import practisejava.others.deepclone.ReferencedObject;
public class ObjectForDeepClone implements Cloneable{
    private int i = 0;
    private ReferencedObject rf = null;
    public void setNum(int i) {
        this.i = i;
    }
    public void setReferencedObject(ReferencedObject rf){
        this.rf = rf;
    }
    public int getNumber(){
        return this.i;
    }
    public ReferencedObject getReferencedObject(){
        return this.rf;
    }
    // 重写Object的clone
    public Object clone() throws CloneNotSupportedException {
         ObjectForDeepClone cloneObject = (ObjectForDeepClone)super.clone();
         // 调用引用对象的clone方法
         if(rf!=null){
             ReferencedObject rfClone = (ReferencedObject)rf.clone();
             cloneObject.setReferencedObject(rfClone);
         }
         return cloneObject;
     }
}
clone测试
package practisejava.others.deepclone;
public class CloneTest  {
    public static void main(String[] args) {
        ObjectForDeepClone ofdc = new ObjectForDeepClone();
        ofdc.setNum(888);
        ofdc.setReferencedObject(new ReferencedObject(1));
        // 开始clone了!
        ObjectForDeepClone deepCloneObject = null;
        try {
            deepCloneObject = (ObjectForDeepClone) ofdc.clone();
        } catch (CloneNotSupportedException ex) {
            System.out.println("Sorry,Clone Not Supported!");
        }
        // 测试clone是否成功
        if(deepCloneObject!=null){
            System.out.println("before clone");
            System.out.println("ObjectForDeepClone get number : " + ofdc.getNumber());
            System.out.println("ObjectForDeepClone ReferencedObject get number : "+ofdc.getReferencedObject().getNumber()+"\n");
            deepCloneObject.setNum(999);
            deepCloneObject.getReferencedObject().doubleValue();
            System.out.println("after clone");
            System.out.println("ObjectForDeepClone get number : " + ofdc.getNumber());
            System.out.println("ObjectForDeepClone ReferencedObject get number : " + ofdc.getReferencedObject().getNumber());
            System.out.println("CloneObject get number : " + deepCloneObject.getNumber());
            System.out.println("CloneObject ReferencedObject get number : " + deepCloneObject.getReferencedObject().getNumber());
        }
    }
}
 
 
运行结果:
before clone
ObjectForDeepClone get number : 888
ObjectForDeepClone ReferencedObject get number : 1
after clone
ObjectForDeepClone get number : 888
ObjectForDeepClone ReferencedObject get number : 1
CloneObject get number : 999
CloneObject ReferencedObject get number : 2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值