Effective Java 之-----谨慎的覆盖clone方法

1、概述

  如果clone方法返回一个由构造器创建的对象,它就得到有错误的类。因此,如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone得到的对象。
如果类的所有超类都遵循这条规则,那么调用super.clone最终会调用Object的clone方法,从而创建出正确的实例。这种机制类似于自动的构造器调用链,只不过它不是强制要求的。

2、示例

需求: 为HashTable编写一个clone方法,它内部数据包含一个HashTable数组,每个HashTable都指向键值对列表的第一个项,如果table是空的,返回null。

代码实现如下:

public class HashTable implements Cloneable {
    private Entry[] buckets;
    
    private static class Entry{
        Object key;
        Object value;
        Entry next;

        Entry(Object key, Object value, Entry next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }
    //......
}

//递归clone这个HashTable数组,如下:
public HashTable clone(){
    HashTable result = new HashTable();
    try {
        result = (HashTable) super.clone();
        result.buckets = buckets.clone();
        return result;
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return result;
}

  虽然被clone对象有它自己的HashTable数组,但这个数组引用的链表与原始对象是一样的,从而很容易引起clone对象和原始对象中的不确定的行为,为了修正这个问题,必须单独的拷贝并组成每个桶的链表,实现如下:

public class HashTable1 implements Cloneable {
    private Entry[] buckets;

    private static class Entry{
        Object key;
        Object value;
        Entry next;

        Entry(Object key, Object value, Entry next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
        
       Entry deepCopy(){
    		Entry result = new Entry(key,value,next);
    		for(Entry p=result;p.next !=null; p=p.next){
        		p.next=new Entry(p.next.key,p.next.value,p.next.next);
   		}
    		return result;
	}
    }
    //......

    public HashTable1 clone(){
        HashTable1 result = new HashTable1();
        try {
            result = (HashTable1) super.clone();
            result.buckets = new Entry[buckets.length];
            for(int i=0;i<buckets.length;i++){
                result.buckets[i] = buckets[i].deepCopy();
            }
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

  步骤:先调用super.clone方法,然后把结果对象中的所有域都设置称他们的空白状态,然后调用高层的方法来重新产生对象的状态。

3、总结

  简而言之,所有实现了Cloneable接口的类都应该用一个公有的方法覆盖clone,此方法首先调用super.clone,然后修正任何需要修正的域。一般情况下,这意味着要拷贝任何包含内部“深层结构”的可变对象,并用指向新对象的引用代替原来指向这些对象的引用。

原文地址:https://www.cnblogs.com/hunterCecil/p/5716073.html