浅拷贝
public class Stack implements Cloneable {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object o) {
ensureCapacity();
elements[size++] = o;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 【避免内存泄漏】
return result;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
// 实现clone方法,浅拷贝
@Override
protected Stack clone() throws CloneNotSupportedException {
return (Stack) super.clone();
}
}
深拷贝
//深拷贝
@Override
protected Stack clone() throws CloneNotSupportedException {
Stack result = (Stack) super.clone();
result.elements = elements.clone(); //对elements元素进行拷贝(引用或基本数据类型)
return result;
}
注意:
- 由于java5.0后引入了协变返回类型(covariant return type)实现(基于泛型),即覆盖方法的返回类型可以是被覆盖方法的返回类型的子类型,所以clone方法可以直接返回Stack类型,而不用返回Object类型,然后客户端再强转。
- 在数组上调用clone返回的数组,其编译时类型与被克隆数组的类型相同。
- 若elements域是final的,深拷贝不能正常工作。因为clone架构与引用可变对象的final域的正常用法是不兼容的。(说明:在调用super.clone()后,final 属性值已经确定,不能在给final属性值改变,编译会不通过)
- 若elements数组中的元素是引用类型,则此方法仅仅是对引用的拷贝,元素指向的还是原来的对象
还应该注意,**数组的clone,仅仅复制的是数组中的元素,即若数组中元素为引用类型,仅仅复制引用。若clone的对象中含有链表,则应单独对链表进行循环复制。**例如,一个内部包含一个散列桶数组的散列表,其数组中每个元素都指向一个独立的链表。此时仅仅使用上面的方法就是不完全拷贝。
代码
public class HashTable implements Cloneable {
private static final int CAPACITY = 10;
//散列桶数组,数组中元素指向由Entry对象组成的链表(指向链表第一个Entry)
private Entry[] buckerts = new Entry[CAPACITY];
public void put(Object key, Object value) {
int index = key.hashCode() % CAPACITY;
Entry e = buckerts[index];
buckerts[index] = new Entry(key,value,e);
}
@Override
public HashTable clone() throws CloneNotSupportedException {
HashTable result = (HashTable)super.clone();
result.buckerts = buckerts.clone(); //仅仅复制了对链表的引用。
return result;
}
//轻量级单链表
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}
改进
@Override
public HashTable clone() throws CloneNotSupportedException {
HashTable result = (HashTable)super.clone();
result.buckerts = buckerts.clone();
for(int i=0; i<buckerts.length; i++) {
result.buckerts[i] = buckerts[i].deepCopy();
}
return result;
}
//轻量级单链表
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
//1、递归实现链表复制
Entry deepCopy() {
return new Entry(key,value,next == null ? null : next.deepCopy());
}
//2、迭代实现链表复制
Entry deepCopy() {
Entry result = new Entry(key, value, next);
for(Entry e = result; e.next != null; e = e.next) {
e.next = new Entry(e.next.key, e.next.value, e.next.next);
}
return result;
}
}
实现clone方法的步骤:
- 首先调用父类的super.clone方法(父类必须实现clone方法),这个方法将最终调用Object的中native型的clone方法完成浅拷贝
- 对类中的引用类型进行单独拷贝
- 检查clone中是否有不完全拷贝(例如,链表),进行额外的复制