码迷,mamicode.com
首页 > 编程语言 > 详细

Java的“影子克隆”和“深度克隆”

时间:2016-08-25 20:29:14      阅读:252      评论:0      收藏:0      [点我收藏+]

标签:

今天来学习学习java对象的克隆,在写代码的时候,有时候我们会这样写:对象1=对象2,也就是把对象2赋值给对象1了,但是这样做有个问题,就是如果我们修改了对象2的属性值,对象1的相同属性值也被修改了,反过来亦如此,让我来证明一下:

public class A {
    private String msg;

    public A() {
        msg = "Hello";
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
public class Main {

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
        System.out.println("a1.msg: " + a1.getMsg());
        a1 = a2;
        a2.setMsg("World");
        System.out.println("a1.msg: " + a1.getMsg());
    }

}

运行结果:

技术分享

从Main类可以看到,一开始我们new了两个对象,也就是在JVM堆内存开辟了两块内存空间,接着把a2赋值给a1,这就意味着a2和a1对象指向了栈内存中同一个引用,这个时候我们修改a2对象的属性,a1对象的属性也同时会改变,反过来亦如此,例如我现在修改a1的属性来试试看a2的属性是否被改变了:

public class Main {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
        System.out.println("a1.msg: " + a1.getMsg());
        a1 = a2;
        a1.setMsg("Java");
        System.out.println("a2.msg: " + a2.getMsg());
    }
}

技术分享

毫无悬念地改变了,也就是说使用“=”操作符会让两个对象指向同一个引用,两个对象的属性值是互通的,一个对象的改变必然会同步到另一个对象上面,但是有时候我们并不想一个对象的改变引起另一个对象的改变,我们希望a2对象被创建后含有a1对象初始的值,但是a2对象属性的改变不会影响a1对象,这样我们就应该使用对象的克隆,对象的克隆又分浅克隆和深克隆,我们先来看看浅克隆的实现方式:

public class A implements Cloneable {
    private String msg;

    public A() {
        msg = "Hello";
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        A a1 = new A();
        System.out.println("a1.msg: " + a1.getMsg());
        A a2 = (A) a1.clone();
        System.out.println("a2.msg: " + a2.getMsg());
        a2.setMsg("Java");
        System.out.println("a1.msg: " + a1.getMsg());
        System.out.println("a2.msg: " + a2.getMsg());
    }
}

技术分享

可以看到实现对象的克隆比较简单,首先该类应该实现Cloneable接口并覆盖Object根类的clone方法,注意Cloneable只是一个标记接口,里面并没有定义任何方法,clone方法里面的实现也很简单,调用父类Object的clone方法就好了。现在看看Main类就知道,a2对象的msg初始化是"Hello",把a2的msg属性的值改为"Java"后,并没有影响a1对象的msg值,其值仍然是"Hello". 我们刚才看到的是浅克隆,没有集合属性,如果有集合属性的话,浅克隆就应付不了这种情况了,我们来看看代码:

public class A implements Cloneable {
    private List<String> list;

    public A() {
        list = new ArrayList<String>();
        list.add("Hello");
        list.add("World");
    }
    
    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        A a1 = new A();
        System.out.println("a1.list: " + a1.getList());
        A a2 = (A) a1.clone();
        System.out.println("a2.list: " + a2.getList());
        a2.getList().add("Java");
        System.out.println("a1.list: " + a1.getList());
        System.out.println("a2.list: " + a2.getList());
    }
}

技术分享

大家看看,神奇的魔咒又出现了,即使我们实现了对象的克隆,但是因为对象的属性是集合类,这样我们修改a2对象的集合类中的值,也一定会影响a1对象集合类的值, 这是因为a1和a2对象的list属性是指向同一个内存地址的,面对这种情况,我们需要使用到对象的深克隆,具体怎么实现呢,来看代码:

public class A implements Cloneable {
    private List<String> list;

    public A() {
        list = new ArrayList<String>();
        list.add("Hello");
        list.add("World");
    }
    
    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    @Override
    protected A clone() throws CloneNotSupportedException {
        A other = (A) super.clone();
        List<String> otherList = new ArrayList<String>();
        for (String s : list) {
            otherList.add(s);
        }
        other.setList(otherList);
        return other;
    }
}
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        A a1 = new A();
        System.out.println("a1.list: " + a1.getList());
        A a2 = (A) a1.clone();
        System.out.println("a2.list: " + a2.getList());
        a2.getList().add("Java");
        System.out.println("a1.list: " + a1.getList());
        System.out.println("a2.list: " + a2.getList());
    }
}

技术分享

大家可以看到,深克隆的实现也就是要在clone方法里面new一个list,并把原来对象list中的值赋值到新list中并把新list设置到新对象,说起来好拗口啊,这样a2.list值得改变不会影响a1.list了,想想如果原始类的属性是一个对象,而对象的属性又有对象和集合类,那么clone方法里面的代码就会很大,因为深克隆必须保证全面的克隆。

 

Java的“影子克隆”和“深度克隆”

标签:

原文地址:http://www.cnblogs.com/stonefeng/p/5808178.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!