Effective Java - clone
第12条 始终要覆盖toString
- 覆盖
toString
可以方便系统调试 -
toString
方法不一定要显示所有信息,应该是显示所有值得关注的信息 - 重写
toString
方法的弊端就是,如果其它人依赖了toString
的返回格式,当返回格式变化的时候,就会导致之前的程序出现异常。不管是否指定了toString
的格式,都要给toString
中包含的信息一个可以访问的API
个人感觉重写toString
的意义不大,真正需要看log的时候,都会直接使用序列化工具或者打印一些核心的字段值,不会选择去重写toString
方法来记录log
第13条 谨慎的覆盖clone
-
一个类如果没有实现
Cloneable
接口,直接重写了clone
方法,并且在clone
方法里调用了super.clone()
,会抛出CloneNotSupportedException
。实现Cloneable
接口是为了提供一个功能适当的公有clone方法 -
只要一个类实现了
Cloneable
,Object的clone方法就会返回对象的逐个字段的拷贝。 -
来自Object规范中的
clone
方法的通用约定:x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
这3点并不是绝对的要求
-
clone
方法的返回值应该是当前类(而不是Object) -
immutable的类不应该提供
clone
方法. -
如果一个类的
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
中的元素都复制一遍,并且新建一个数组 -
对于比较复杂的数据结构,会需要递归
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; }
-
公有的
clone
方法应该省略throws,为了继承而设计的类不应该实现Cloneable
-
如果需要线程安全的
Cloneable
方法,需要使用synchronized
-
另一个实现对象拷贝的方法(更好的方法)是提供一个拷贝构造器或者拷贝工厂:
public Yum(Yum yum); public static Yum newInstance(Yun yum);
以下是个人观点了:
clone方法的使用,风险还是很高的。之前看到过有人推荐使用序列化进行clone,尝试过使用,体验下来感觉不错,因为不涉及复杂的继承关系,没碰到问题。真正的使用场景中,clone方法应该更多地会用于vo的复制,而vo又是简单的基本数据类型的组合,用序列化会比较合适一点。
clone方法在TMS中是有过使用的,具体可以等下次review的时候分享