谨慎覆盖Clone

本文探讨了Java中对象拷贝的问题,对比了赋值语句、浅拷贝和深拷贝的区别,通过具体示例说明了如何实现深拷贝。

    假设现在有个需求:一个对象A,在某一时刻A中已包含了一些有效值,此时可能需要一个和A完全相同对象B,并且此后对B的任何改动不会影响到A的值,也就算是说,A与B是两个相对独立的对象,但B的初始值是有A对象确定的。考虑下赋值语句是否可以达到此效果?

    在Java中赋值语句是达不到此效果的。

Person person1 = new Person("Jim", "123456");
Person person2 = person1;


    从图中可以看出用赋值语句,两个对象实际指向的是同一个引用,指向的是同一块地址空间。

    其实我们可以使用clone()来达到上述目的。


public class CloneClass implements Cloneable {
    private int id;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        CloneClass o = null;
        try {
            o = (CloneClass) super.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }   
}
    实现clone()的步骤


1.期望实现clone()的Class实现了Cloneable Interface.

2.重载了clone()方法。在clone()中调用super的clone().无论需实现clone()的Class继承结构是什么样的。super.clone()都直接或间接调用了java.lang.Object类的clone().

    至于为什么不直接new一个对象,因为Object类的clone()是一个native方法,native方法的效率远高于Java的非native方法。    

    Shadow Clone(影子Clone)

    Shadow clone 并不是对对象的完整clone,换句话说clone后两个对象A和B还会中的某些对象指向的对象是同一个。如下面ShadowTest的实例test1和test2中的mArrayList指向的是同一个对象。


public class ShadowTest implements Cloneable {
    private int mId;
    private ArrayList mArrayList = new ArrayList();
    
    public void setId(int id) {
        mId = id;  
    }
    
    public void add(int x) {
        mArrayList.add(x);
    }
    
    @Override
    public String toString() {
        return String.valueOf(mId) + ";" + mArrayList.toString();
    }

    @Override
    protected Object clone() {
        ShadowTest test = null;
        try {
            test = (ShadowTest) super.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return test;
    }
}


public class ShadowClone {
    
    public static void main(String[] args) {
        ShadowTest test1 = new ShadowTest();
        test1.setId(5);
        test1.add(30);
        System.out.println("test1 = " + test1.toString());
        
        ShadowTest test2 = (ShadowTest) test1.clone();
        System.out.println("test2 = " + test2.toString());
        
        test2.setId(3);
        System.out.println("test1 = " + test1.toString());
        System.out.println("test2 = " + test2.toString());
        
        test2.add(31);
        System.out.println("test1 = " + test1.toString());
        System.out.println("test2 = " + test2.toString());
    }

}
运行结果:


test1 = 5;[30]
test2 = 5;[30]
test1 = 5;[30]
test2 = 3;[30]
test1 = 5;[30, 31]
test2 = 3;[30, 31]

Deep Clone

与Shadow Clone不同,Deep Clone是对对象的完整clone。


public class DeepTest implements Cloneable {
    private int mId;
    private ArrayList mArrayList = new ArrayList();
    
    public void setId(int id) {
        mId = id;  
    }
    
    public void add(int x) {
        mArrayList.add(x);
    }
    
    @Override
    public String toString() {
        return String.valueOf(mId) + ";" + mArrayList.toString();
    }

    @Override
    public Object clone() {
        DeepTest test = null;
        try {
            test = (DeepTest) super.clone();
            test.mArrayList = (ArrayList) mArrayList.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return test;
    }
}
public class DeepClone {
    
   public static void main(String[] args) {
        DeepTest test1 = new DeepTest();
        test1.setId(5);
        test1.add(30);
        System.out.println("test1 = " + test1.toString());
        
        DeepTest test2 = (DeepTest) test1.clone();
        System.out.println("test2 = " + test2.toString());
        
        test2.setId(3);
        System.out.println("test1 = " + test1.toString());
        System.out.println("test2 = " + test2.toString());
        
        test2.add(31);
        System.out.println("test1 = " + test1.toString());
        System.out.println("test2 = " + test2.toString());
    }
} 
运行结果:


test1 = 5;[30]
test2 = 3;[30]
test1 = 5;[30]
test2 = 3;[30, 31]

从上面例子来看,其实Object类的clone()只是对对象地址空间的简单复制,对于类中的复杂对象及数据结构不很好的复制,需要实现者调用复杂对象或者数据结构的clone()来达到Deep Clone。

另外为了避免误用,可以增添如deepCopy()的函数来实现Deep Clone.

转载于:https://my.oschina.net/shaorongjie/blog/127731

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值