覆盖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接口

Java - 谨慎覆盖clone的更多相关文章

  1. Effective Java —— 谨慎覆盖clone

    本文参考 本篇文章参考自<Effective Java>第三版第十三条"Always override toString",在<阿里巴巴Java开发手册>中 ...

  2. Java - 谨慎覆盖equals

    平时很难遇到需要覆盖equals的情况. 什么时候不需要覆盖equals? 类的每个实例本质上是唯一的,我们不需要用特殊的逻辑值来表述,Object提供的equals方法正好是正确的. 超类已经覆盖了 ...

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

    1.概述 如果clone方法返回一个由构造器创建的对象,它就得到有错误的类.因此,如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone得到的对象.如果类的所有超类都 ...

  4. Item 11 谨慎地覆盖Clone

    1.进行浅拷贝时,只是复制原始数据类型的值,则可以通过覆盖Clone方法来达到.另外,在进行浅拷贝的时候,还要注意,成员对象中不应该要有引用类型,如果有引用类型,那么,进行了浅拷贝之后,两个对象将会共 ...

  5. 第11条:谨慎地覆盖clone

    Clone提供一种语言之外的机制:无需调用构造器就可以创建对象. 它的通用约定非常弱: 创建和返回该对象的一个拷贝.这个拷贝的精确含义取决于该对象的类.一般含义是,对于任何对象x,表达式x.clone ...

  6. 详解Java中的clone方法

    详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...

  7. 浅拷贝和深拷贝(谈谈java中的clone)

    clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有 ...

  8. java克隆对象clone()的使用方法和作用

    转自:997.html">http://www.okrs.cn/blog/news/?997.html 内容摘要 若需改动一个对象,同一时候不想改变调用者的对象.就要制作该对象的一个本 ...

  9. 深入理解Java中的Clone与深拷贝和浅拷贝

    1.Java对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象. ...

随机推荐

  1. softmax,softmax loss和cross entropy的讲解

    1 softmax 我们知道卷积神经网络(CNN)在图像领域的应用已经非常广泛了,一般一个CNN网络主要包含卷积层,池化层(pooling),全连接层,损失层等.这一篇主要介绍全连接层和损失层的内容, ...

  2. python基础,变量,if语句

     一.python初识 python是一门 解释型弱类型编程语言. 特点: 简单.明确.优雅 二.python的解释器 CPython. 官方提供的. 内部使用c语言来实现 PyPy. 一次性把我们的 ...

  3. HDU6333-2018ACM暑假多校联合训练1002-Harvest of Apples-莫队+费马小定理

    题意很简单啦,求S(n,m)的值 通过打表我们可以知道 S(n + 1, m) = S(n, m) * 2 - C(n, m); S(n - 1, m) = (S(n, m) + C(n - 1, m ...

  4. [AIR] 检测移动设备运动

    Accelerometer 类根据由设备的运动传感器检测的活动调度事件.此数据表示设备的位置或沿三维轴的移动.当设备移动时,传感器会检测到此移动并返回加速数据.Accelerometer 类提供了各种 ...

  5. 恢复 MSSQL bak 文件扩展名数据(下)

    恢复 MSSQL bak 文件扩展名数据 一.概念: RESTORE Statements (Transact-SQL) Restores backups taken using the BACKUP ...

  6. [转] HTTP状态码错误代码

    一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务不可用 详细分解: 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码. 代码 说明 ...

  7. 2016级算法第五次上机-F.ModricWang的水系法术

    1066 ModricWang的水系法术 思路 比较典型的最大流问题,需要注意的是,题目已经暗示(明示)了这里的边是双向的,在建图的时候需要加上反向边的容量值. 解决最大流问题的基本思路就是不断在残量 ...

  8. 2016级算法第二次上机-D.Bamboo的饼干

    Bamboo的饼干 分析 从两个数组中各取一个数,使两者相加等于给定值.要注意去重和排序 难度不大,方法很多,基本只要不大于O(n^2 ) 的都可以过.本意想考察二分搜索 还可以借助stl中的map, ...

  9. iOS8 dismissViewControllerAnimated:YES在Show Segue不工作

    最近使用iOS8 Sdk(xcode6.1.1)使用Show Segue功能,V_A视图push到V_B视图,然后想通过使用按键返回V_A,我记得ios6的时候可以使用[self dismissVie ...

  10. Saiku2.6 配置数据源

    把连接字符串和Schema文件放这里.