页面

2010-07-17

Java clone方法既深copy

Object类中的clone方法是protected的,要提供给外部使用,必须(直接或间接)实现标志接口java.lang.Cloneable。

容器类(ArrayList等)提供的clone方法只是浅copy(shallow copy),即重新生成并保存了指向目标类的一系列指针。要实现深copy,必须通过新容器对象的添加方法添加目标对象的clone。如:list_x.add(obj.clone())。即目标对象应很好地实现clone方法。但目前,传统的clone方法设计模式在泛型编程时会出现问题:
public class Foo<T extends Cloneable> {
    T obj;
    public Object clone() {
        obj.clone();
    }
}
这段代码无法通过编译:obj继承有Object的clone方法,但不能从外部调用;Cloneable是个空的、标志接口,没有声明clone方法。所以,obj没有公开的clone方法供调用。

一个设计策略是由Cloneable接口派生一个接口,其中声明clone方法,然后泛型约束到该接口。如下列代码所示:
public interface IClone implements Cloneable {
    public Object clone();
}

public class Foo<T extends IClone> {
    T obj;
    public Object clone() {
        obj.clone();
    }
}

一个更加健壮的clone方法实现是通过反射技术,如下列代码:
public MyClass<T> clone( ) throws CloneNotSupportedException { 

    MyClass<T> clon = null; 

    try {  
         clon = (MyClass<T>)super.clone(); 
    } catch (CloneNotSupportedException e) {  
         throw new InternalError();  
    } 

    try { 
       Class<?> clzz = this.field_a.getClass(); 
       Method meth = clzz.getMethod("clone", new Class[0]); 
       Object dupl = meth.invoke(this.field_a, new Object[0]); 
       clon.field_a = (T)dupl;  
    } catch (Exception e) { 
       throw new CloneNotSupportedException (e.toString()); 
    }

}

实际编程中,很难事先设计周全,经常遇到涉及类没有完整实现clone方法的情况,此时,可使用下列通过序列化技术实现的深copy方法(注意:此方法要求被clone的对象实现java.io.Serializable接口):
import java.io.*;
import java.util.*;

/**
   This class is a tool to clone an object deeply.
*/
public class Cloner {

    private Cloner() {
    }

    /**
       This method returns a deep copy of an object.
       @param src the source object to be cloned
       @return a deep copy of src
    */
    public static Object clone(Object src) throws Exception {

 ObjectOutputStream oos = null;
 ObjectInputStream ois = null;

 try {

     ByteArrayOutputStream bos = new ByteArrayOutputStream();
     oos = new ObjectOutputStream(bos);
     // serializing and passing the object
     oos.writeObject(src);
     oos.flush();
     ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
     ois = new ObjectInputStream(bin);
     // returning the new object
     return ois.readObject();

 } catch (Exception e) {
     System.out.println("Exception in Cloner: " + e);
     throw(e);
 } finally {
     oos.close();
     ois.close();
 }
    }

}

没有评论: