覆盖clone时需要实现Cloneable接口,Cloneable并没有定义任何方法。
那Cloneable的意义是什么?
如果一个类实现了Clonable,Object的clone方法就可以返回该对象的逐域拷贝,否则会抛出CloneNotSupportedException

通常,实现接口是为了表明类的行为。
而Cloneable接口改变了超类中protected方法的行为。
这是种非典型用法,不值得仿效。

好了,既然覆盖了clone方法,我们需要遵守一些约定:

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

另外,我们必须保证clone结果不能影响原始对象的同时保证clone方法的约定。

比如下面这种情况,没有覆盖clone方法,直接得到super.clone()的结果:

import java.util.Arrays;

public class Stack implements Cloneable {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
} public void push(Object e) {
ensureCapacity();
elements[size++] = e;
} public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
} public boolean isEmpty() {
return size == 0;
} // Ensure space for at least one more element.
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
} }

结果可想而知,clone结果的elements和原始对象的elements引用同一个数组。

既然如此,覆盖clone方法,并保证不会伤害到原始对象:

@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}

虽然把elements单独拿出来clone了一遍,但这种做法的前提是elements不是final。
其实再正常不过,clone无法和引用可变对象的不可变field兼容。

如果数组的元素是引用类型,当某个元素发生改变时仍然会出现问题。
此处以Hashtable为例,Hashtable中的元素用其内部类Entry。

private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;
final K key;
V value;
Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
} //..
}

如果像Stack例子中那样直接对elements进行clone,某个Entry发生变化时clone出来的Hashtable也随之发生变化。

于是Hashtable中如此覆盖clone:

/**
* Creates a shallow copy of this hashtable. All the structure of the
* hashtable itself is copied, but the keys and values are not cloned.
* This is a relatively expensive operation.
*
* @return a clone of the hashtable
*/
public synchronized Object clone() {
try {
Hashtable<K,V> t = (Hashtable<K,V>) super.clone();
t.table = new Entry[table.length];
for (int i = table.length ; i-- > 0 ; ) {
t.table[i] = (table[i] != null)
? (Entry<K,V>) table[i].clone() : null;
}
t.keySet = null;
t.entrySet = null;
t.values = null;
t.modCount = 0;
return t;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}

鉴于clone会导致诸多问题,有两点建议:

  • 不要扩展Cloneable接口
  • 为继承而设计的类不要实现Cloneable接口

最新文章

  1. 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸
  2. Java判断字符串是否是数值
  3. openlayers优化项
  4. spring知识大全(4)
  5. docker on centos
  6. mysql下优化表和修复表命令使用说明(REPAIR TABLE和OPTIMIZE TABLE)
  7. iOS 静态类库 打包 C,C++文件及和OC混编
  8. 弹出框--self
  9. jq获取元素到底部的距离
  10. tyvj 1342 教主泡嫦娥 环上DP
  11. SpringMVC4+thymeleaf3的一个简单实例(篇三:页面参数获取)
  12. JAVA并发,锁与方法
  13. HUNNU11352:Digit Solitaire
  14. 安卓高级3 RecyclerView结合SwipeRefreshLayout并添加上拉
  15. Rational Rose正逆向工程(类图转Java代码,Java代码转类图)
  16. iOS中判断照片和相机权限
  17. Microsoft Graph: Developer Blog
  18. Python【每日一问】05
  19. ES6语法篇(其一)
  20. JS - 查找同辈中的对象

热门文章

  1. 谈谈iOS开发如何写个人中心这类页面--静态tableView页面的编写
  2. Linux下的find查找命令
  3. [Flex] 组件Tree系列 —— 支持CheckBox组件
  4. django2使用xadmin打造适合国人的后台管理系统(1)
  5. fetch网络请求 get 和 post
  6. node.js调试方法
  7. angular5给懒加载模块添加loading
  8. 高阶篇:4.1)QFD质量功能展开-总章
  9. 洛谷 P3227 [HNOI2013]切糕(最小割)
  10. Go语言介绍