Java Clone

一变二

场景


对象复制的实现目标是

  1. 生成新对象 x.clone() != x
  2. 对象类别一致 x.clone().getClass() == x.getClass()
  3. 对象相等 x.clone().equals(x)

实现


clone是Object类的基本方法之一。
从java.lang.Object类中可以发现并非由Java代码实现,clone并没有调用类构造函数而是进行二进制流拷贝

Object.java
1
protected native Object clone() throws CloneNotSupportedException;

使用类必须实现java.lang.Cloneable接口,Cloneable是一个不包含方法的标识性接口

Cloneable.java
1
2
public interface Cloneable {
}

Java原生的java.util.Calendar就是很好的范例

Calendar.java
1
2
3
4
5
6
7
8
9
10
11
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
public final static int ERA = 0;
//...
protected int fields[];
protected boolean isSet[];
transient private int stamp[];
protected long time;
//...
private TimeZone zone;
//...
}

clone方法的实现原则是调用父类clone方法,在继承链条上最终会调用到Object中的实现。Object提供的clone是浅拷贝机制,因此涉及可变成员的情况下,默认的clone结果是新旧对象持有相同的可变成员引用,新旧对象并没有完全独立。

为了摆脱可变对象的共享,实现深拷贝,子类要重新实现clone方法。首先用super.clone获取基础,然后强制转换成子类,再把子类的可变属性进行重新赋值。
从实现可以看出常量和long这种基本类型无需特殊逻辑,而数组和可变类TimeZone需要重新赋值,对于数组而言,直接构造新数组,对于可变类,直接交给该类的clone方法。

Calendar.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public Object clone()
{
try {
Calendar other = (Calendar) super.clone();

other.fields = new int[FIELD_COUNT];
other.isSet = new boolean[FIELD_COUNT];
other.stamp = new int[FIELD_COUNT];
for (int i = 0; i < FIELD_COUNT; i++) {
other.fields[i] = fields[i];
other.stamp[i] = stamp[i];
other.isSet[i] = isSet[i];
}
//内部对象也要clone
other.zone = (TimeZone) zone.clone();
return other;
}
catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}

优缺点


clone要完全利用clone机制实现深拷贝,需要继承链范围内所有成员都实现Cloneable接口并重写clone方法,代价很大。

替代方法:

  • 利用序列化: 如果实现了序列化接口,可利用commons-lang SerializationUtils类
  • 利用反射:如果没实现序列化接口,只能利用第三方类库通过反射获取信息进行构建


  • Kryo
  • Spring BeanUtil