第11条:谨慎地覆盖clone

第11条:谨慎地覆盖clone

调用clone()方法需要对象实现Cloneable接口,但是这个接口有着许多缺陷。最主要的缺陷在于,Cloneable接口中不包含任何方法,而Object中的clone方法是protect的,也就是说如果一个类只是继承了Cloneable接口,但是却没有重写clone()方法,那么对于这个类的对象,clone()方法依然是不可用的。

既然Cloneable接口中没有任何方法,那么它有什么作用呢,可以看看Object中的clone()方法:

protected Object clone() throws CloneNotSupportedException {
    if (!(this instanceof Cloneable)) {
        throw new CloneNotSupportedException("Class " + getClass().getName() +" doesn't implement Cloneable");
    }
    return internalClone();
}

从上面的代码中可以看出Cloneable接口实际上决定了clone()方法的行为,按书中所说:“这是接口的一种极端非典型的用法,并不值得仿效。”

也就是说如果clone()方法想要实现预想中的效果,就需要遵守一个“相当复杂的,不可实施的,并且基本上没有文档说明的协议”。

但是即使是java.lang.Object规范中的约定内容[JavaSE6]中也存在着许多问题,例如:“不调用构造器”,这一条规定太过强硬,因为行为良好的clone()方法可以调用构造器来创建对象,构造之后在复制内部数据。

在最理想的状况下,如果需要使用clone方法,只需要实现Cloneable接口,并且重写clone()方法即可:

@Override
public Object clone(){
    try{
        return super.clone();
    }catch(CloneNotSupportedException e){
        throw new AssertionError();
    }
}

但是这种方法只适用于需要浅拷贝的情况。但如果这么做,而且拷贝的类中存在可变的对象,就会导致灾难性的结果。

浅拷贝:浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

例如我们希望通过上面的方式clone一个如下的Stack:

public class Stack{
    private Object[] elements;
    private int size = 0;

    //other......
}

这样clone出来的Stack对象与原Stack对象却使用的是同一个elements引用,但是size属性却是相互独立的。这样很快就会发现这两个Stack对象已经无法正常工作。

对于这种情况,我们可以通过深拷贝来避免这种情况的发生,例如上面的Stack类,虽简单的做法就是在elements数组中递归地调用clone:

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

克隆复杂对象还有一个方法

先调用super.clone(),然后把返回对象中的所有域都设为空白状态,再将原对象中各个域的值赋给克隆对象中的相应部分,但是它运行起来通常没有“直接操作对象及其克隆对象的内部状态的clone方法”快。

正是因为 Cloneable 接口有着这么多的问题,可以肯定的说,其他接口都不应该扩展此接口;那些为了继承而设计的类也不应该实现此接口。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值