Object中的clone方法定义如下:
protected native Object clone() throws CloneNotSupportedException;
定义一个可克隆的对象,一般的步骤如下:
- implements Cloneable;
- override clone()
Example
//Person及其子类都有了克隆能力
class Person implements Cloneable {
private int age;
private String name;
Person(int age, String name){
this.age = age;
this.name = name;
}
public String getName() {
return name;
}
@Override
public Object clone() {
try {
//实际调用了Object的clone()
return super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name.hashCode() + '\'' +
'}';
}
}
如果调用Person personOfClone = person.clone(),实际上进行的是 shallow copy,即person和personOfClone中的name引用的是同一块内存地址。对personOfClone的修改可能会影响到person。那么如何进行 deep copy?若要实现 deep copy,则必须对对象内部的引用进行clone(前提是这些引用是可克隆的)直到所有的引用都进行了clone。
默认浅克隆是合理的,因为不能确定所有的引用都是可以克隆的。
通过序列化实现 deep copy
Example
public class Compete {
public static final int SIZE = 25000;
public static void main(String[] args) throws Exception {
Thing2[] a = new Thing2[SIZE];
for (int i = 0; i < a.length; i++)
a[i] = new Thing2();
long t1 = System.currentTimeMillis();
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(buf);
for (int i = 0; i < a.length; i++)
o.writeObject(a[i]);
// Now get copies:
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
Thing2[] c = new Thing2[SIZE];
for (int i = 0; i < c.length; i++)
//这里实际上已经进行了copy
c[i] = (Thing2) in.readObject();
long t2 = System.currentTimeMillis();
System.out.println("Duplication via serialization: " +
(t2 - t1) + " Milliseconds");
//clone
Thing4[] b = new Thing4[SIZE];
for (int i = 0; i < b.length; i++)
b[i] = new Thing4();
t1 = System.currentTimeMillis();
Thing4[] d = new Thing4[SIZE];
for (int i = 0; i < d.length; i++)
d[i] = (Thing4) b[i].clone();
t2 = System.currentTimeMillis();
System.out.println("Duplication via cloning: " +
(t2 - t1) + " Milliseconds");
}
static class Thing1 implements Serializable {
}
static class Thing2 implements Serializable {
Thing1 o1 = new Thing1();
}
static class Thing3 implements Cloneable {
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.err.println("Thing3 can't clone");
}
return o;
}
}
static class Thing4 implements Cloneable {
private Thing3 o3 = new Thing3();
public Object clone() {
Thing4 o = null;
try {
o = (Thing4) super.clone();
} catch (CloneNotSupportedException e) {
System.err.println("Thing4 can't clone");
}
// Clone the field, too:
o.o3 = (Thing3) o3.clone();
return o;
}
}
}
运行后发现,直接clone的速度要远快于序列化
控制克隆能力
上述例子中,一个对象首先要实现Cloneable对象,然后覆写clone()才能具有克隆能力,那么为何这样设计?
- 假如Object.clone()是public的
实际上,java起初是这样设计的,Object中添加了public 的clone(),那么任何对象都可以克隆了,但是如果每个对象都是可以随意克隆的,就会出现安全性问题(你不一定愿意别人克隆你的对象);今天看到的样子,是做了许多修补之后的版本:Object 中的 clone()被声明为 protected,你必须重载它、实现 Cloneable 接口、并做异常处理。 - 假如Object.clone()是private的
这样的话,子类都不能使用Object中的native方法了。
为何调用Object.clone()
调用Object.clone()时实际会发生什么,致使你重载clone()时必须要调用super.clone() 呢? 原因大概如下:
- Object 类的 clone()方法负责创建正确容量的存储空间,并将属性值由原对象复制到新对象的存储空间中;
- 此方法是native的,效率很高;
- 可以防止不能克隆的对象进行克隆;此方法会在运行期间通过Cloneable对象判断对象是否可以克隆,如果不能克隆,则会抛异常。所以,若子类的方法没有调用Object.clone(),那么实际上是不用实现Cloneable接口的。但为了使具备克隆能力的对象保持一致性,可克隆的对象应该都实现该接口。
网友评论