Effective Java - clone

2022-05-15  本文已影响0人  DZQANN

第12条 始终要覆盖toString

  1. 覆盖toString可以方便系统调试
  2. toString方法不一定要显示所有信息,应该是显示所有值得关注的信息
  3. 重写toString方法的弊端就是,如果其它人依赖了toString的返回格式,当返回格式变化的时候,就会导致之前的程序出现异常。不管是否指定了toString的格式,都要给toString中包含的信息一个可以访问的API

个人感觉重写toString的意义不大,真正需要看log的时候,都会直接使用序列化工具或者打印一些核心的字段值,不会选择去重写toString方法来记录log

第13条 谨慎的覆盖clone

  1. 一个类如果没有实现Cloneable接口,直接重写了clone方法,并且在clone方法里调用了super.clone(),会抛出CloneNotSupportedException。实现Cloneable接口是为了提供一个功能适当的公有clone方法

  2. 只要一个类实现了Cloneable,Object的clone方法就会返回对象的逐个字段的拷贝。

  3. 来自Object规范中的clone方法的通用约定:

    • x.clone() != x
    • x.clone().getClass() == x.getClass()
    • x.clone().equals(x)

    这3点并不是绝对的要求

  4. clone方法的返回值应该是当前类(而不是Object)

  5. immutable的类不应该提供clone方法.

  6. 如果一个类的clone返回的实例不是通过super.clone获得的,那它子类调用super.clone会报错。如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone而得到的对象。

    书中提到了一个例子:

    public class Stack {
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    }
    

    对于这样一个类,如果知识实现了Cloneable接口,调用了super.clone(),那么elements就会是老的对象的引用,这样就会导致会面两个对象公用同一个elements的副作用。

    所以在覆盖clone的时候,一定要把elements中的元素都复制一遍,并且新建一个数组

  7. 对于比较复杂的数据结构,会需要递归clone

    public class HashTable impement Cloneable {
        private Enrty[] buckets = ...;
        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;
        }
    
        Entry deepCopy() {
            return new Enrty(key, value, next == null ? null : next.deepCopy());
        }
    }
    
    @Override
    public HashTable clone() {
        try {
            HashTable result = (HashTable) super.clone();
            result.bucket = new Entry[bucket.length];
            for(int i = 0; i < buckets.length; i++) {
                if (buckets[i] != null)
                    result.buckets[i] = buckets[i].deepCopy();
                return result;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    }
    
    
    

    这里可以很明显看到,如果buckets的元素太多,会导致栈溢出,比较好的方法是通过迭代进行clone

    Entry deepcopy() {
      Entry result = new Entry(key, value, next);
      for (Entry p = result; p.mext != null; p = p.next()) {
        p.next = new Entry(p.nexy.key, p.next.value, p.next.next);
      }
      return result;
    }
    
  8. 公有的clone方法应该省略throws,为了继承而设计的类不应该实现Cloneable

  9. 如果需要线程安全的Cloneable方法,需要使用synchronized

  10. 另一个实现对象拷贝的方法(更好的方法)是提供一个拷贝构造器或者拷贝工厂:

    public Yum(Yum yum);
    public static Yum newInstance(Yun yum);
    

以下是个人观点了:

clone方法的使用,风险还是很高的。之前看到过有人推荐使用序列化进行clone,尝试过使用,体验下来感觉不错,因为不涉及复杂的继承关系,没碰到问题。真正的使用场景中,clone方法应该更多地会用于vo的复制,而vo又是简单的基本数据类型的组合,用序列化会比较合适一点。

clone方法在TMS中是有过使用的,具体可以等下次review的时候分享

上一篇下一篇

猜你喜欢

热点阅读