java中的clone机制

来源:互联网 发布:福州市各县区经济数据 编辑:程序博客网 时间:2024/05/16 06:55

为什么要使用clone?

在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在这种情况下常常使用clone,但解决上述问题方法很多,如先new一个类,然后把原始对象中的信息赋到新对象中,那为什么需要clone呢?

(1)实现clone方法简单、方便

(2)Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非native方法,因此clone方法是高效的

影子clone与深度clone

由于java中 ,对非基本类型变量,它们保存的仅仅是对象的引用,所以使用上要特别小心。请看以下一组代码

public class Sheep implements Cloneable{
    
private String name;
    
private int number;
    
public void setName(String arg) {
        name 
= arg;
    }

    
public String getName() {
        
return name;
    }

    
public Object clone() throws CloneNotSupportedException {
        
return super.clone();
    }

}


public class Sheepfold implements Cloneable {
    
public Sheep sheep;
    
public String name;
    
public int number;
    
public Sheepfold() {
        sheep 
= new Sheep();
    }

    
public Object clone() throws CloneNotSupportedException {
        
return super.clone();
    }

}


public class Main {
  
public static void main(String[] args) throws Exception {
      Sheepfold fold 
= new Sheepfold();
      fold.name 
= "小羊圈";
      fold.number
=10;
           fold.sheep.setName(
"小羊");
           Sheepfold fold2 
= (Sheepfold)fold.clone();
           System.out.println(
"  fold2.name = " + fold2.name);
           System.out.println(
"  fold2.number = " + fold2.number);
           System.out.println(
"  fold2.sheep.getName() = " + fold2.sheep.getName());
           fold2.name 
= "大羊圈";
           fold2.sheep.setName(
"大羊");
           fold2.number
=100;
           System.out.println(
"=====================================");
           System.out.println(
"  fold2.name = " + fold2.name);
           System.out.println(
"  fold2.number = " + fold2.number);
           System.out.println(
"* fold2.sheep.getName() = " + fold2.sheep.getName());
           System.out.println(
"  fold.name = " + fold.name);
           System.out.println(
"  fold.number = " + fold.number);
           System.out.println(
"* fold.sheep.getName() = " + fold.sheep.getName());
           System.out.println(
"=====================================");
    }

}

输出结果

 fold2.name = 小羊圈
 flod2.number = 10
  fold2.sheep.getName() = 小羊
=====================================
  fold2.name = 大羊圈
  flod2.number = 100
* fold2.sheep.getName() = 大羊
  fold.name = 小羊圈
  flod2.number = 10
* fold.sheep.getName() = 大羊
=====================================

在此之前,我们只对fold2.sheep的name赋过值。为什么fold.sheep的name也变为了“大羊”呢?原因很简单,因为它们是指向同一个对象的不同引用。从中可以看出,调用Object类中clone()方法时,首先在内存中划分一块同原对象相同的空间,然后将原对象的内容原样拷贝至新对象。我们知道,java中有基本数据类型,对于基本数据类型,这样的操作是没有问题的,但对非基本类型变量,它们保存的仅仅是对象的引用,这也是为什么clone后非基本类型变量和原对象中的变量指向同一个对象的原因。可能你已经注意到,程序中用到了String类型,即对象,为什么没有出现引用指向同一地址的情况?这是因为String是一个不可更改的类(immutable class),每次给它赋值时,都会产生一个新的String对象。如String str = "a"; str += "b";在这两句代码中,当执行str += "b"时,实际上是重新成生了一个值为“ab”的String对象,即重新分配了一块内存空间。以上clone方法通常被称为“影子clone”。“影子clone”给我们留下了一个问题,即多个引用指向同一个对象。如何解决该问题呢?答案为“深度clone”。把上面的例子改成深度clone很简单,只需将Sheepfold的clone()方法改为如下即可:
    public Object clone() throws CloneNotSupportedException {
        Sheepfold fold = (Sheepfold)super.clone();
        sheep = (Sheep)fold.sheep.clone();
        return fold;
    }