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();
}
}
}