java 深克隆及浅克隆

来源:互联网 发布:铸造行业 利润 知乎 编辑:程序博客网 时间:2024/06/05 17:17

我继续在看java的基础知识,刚看完java的深克隆及浅克隆,就写篇博客记录下。

应用场景:
1.有时候,你需要克隆一个对象,但是对新克隆的对象的操作不会影响原对象。
2. 了解下深克隆,浅克隆,对java加深了解。

//对象引用的复制

Person p = new Person(23, "zhang");Person p1 = p;System.out.println(p);System.out.println(p1);结果是:test.deepClone.Person@2a84aee7test.deepClone.Person@2a84aee7可以看出来两个对象的内存地址是一样的。那肯定是一个对象。p,p1只是两个引用而已。都指向了一个对象,可以把这种现象叫做引用的复制。

//对象的拷贝

Person p = new Person(23, "zhang");Person p1 = (Person) p.clone();System.out.println(p);System.out.println(p1);结果是:test.deepClone.Person@2a84aee7test.deepClone.Person@a09ee92打印出不同的内存地址。说明实现了对象的克隆。
//person 类public class Person implements Cloneable{    private int age;    private String name;    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Person(int age, String name){        this.age = age;        this.name = name;    }    protected Object clone() throws CloneNotSupportedException {        return (Person)super.clone();    }}

记得实现Cloneable接口

我们为什么要实现Cloneable接口呢?其实Cloneable接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。

下面我再举例子来说明

public class MyTestCloneAbnormal implements Cloneable {    private String name;    private Email email;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public MyTestCloneAbnormal(String name, Email email) {        this.name = name;        this.email = email;    }    public Object clone() throws CloneNotSupportedException {        //返回clone的对象        return (MyTestCloneAbnormal) super.clone();    }    static class Email implements Cloneable{        private String contet;        private String title;        public String getContet() {            return contet;        }        public void setContet(String contet) {            this.contet = contet;        }        public String getTitle() {            return title;        }        public void setTitle(String title) {            this.title = title;        }        public Email(String title, String content) {            this.title = title;            this.contet = content;        }    }    public static void main(String[] args) throws CloneNotSupportedException {        MyTestCloneAbnormal p = new MyTestCloneAbnormal("yu", new Email("9.6中午会议", "今天中午十二点,大家在3楼会议室开会"));        MyTestCloneAbnormal p1 = (MyTestCloneAbnormal) p.clone();        p1.setName("张三");        MyTestCloneAbnormal p2 = (MyTestCloneAbnormal) p.clone();        p2.setName("李四");        System.out.println("给[ " + p.getName() + " ]的邮件: " + p.email.getTitle() + "正文是:[" + p.email.getContet() + "]");        System.out.println("给[ " + p1.getName() + " ]的邮件: " + p1.email.getTitle() + "正文是:[" + p1.email.getContet() + "]");        System.out.println("给[ " + p2.getName() + " ]的邮件: " + p2.email.getTitle() + "正文是:[" + p2.email.getContet() + "]");    }}

我是看的一个教程写的。如果你要给三个员工发邮件。clone p对象。得到p1,p2,打印的结果是:

[ yu ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]

如果我想让 yu早来半个小时,这个时候main方法是

    public static void main(String[] args) throws CloneNotSupportedException {    MyTestCloneAbnormal p = new MyTestCloneAbnormal("yu", new Email("9.6中午会议", "今天中午十二点,大家在3楼会议室开会"));    MyTestCloneAbnormal p1 = (MyTestCloneAbnormal) p.clone();    p1.setName("张三");    MyTestCloneAbnormal p2 = (MyTestCloneAbnormal) p.clone();    p2.setName("李四");    System.out.println("给[ " + p.getName() + " ]的邮件: " + p.email.getTitle() + "正文是:[" + p.email.getContet() + "]");    System.out.println("给[ " + p1.getName() + " ]的邮件: " + p1.email.getTitle() + "正文是:[" + p1.email.getContet() + "]");    System.out.println("给[ " + p2.getName() + " ]的邮件: " + p2.email.getTitle() + "正文是:[" + p2.email.getContet() + "]");    //给p对象设置email内容    p.email.setContet("yu,你需要早到半小时");    System.out.println("给[ " + p.getName() + " ]的邮件: " + p.email.getTitle() + "正文是:[" + p.email.getContet() + "]");    System.out.println("给[ " + p1.getName() + " ]的邮件: " + p1.email.getTitle() + "正文是:[" + p1.email.getContet() + "]");    System.out.println("给[ " + p2.getName() + " ]的邮件: " + p2.email.getTitle() + "正文是:[" + p2.email.getContet() + "]");}

结果如下所示,

[ yu ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ yu ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时][ 张三 ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时][ 李四 ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时]

张三,李四的邮件内容也变了。。这就说明,关于对象clone,clone的是对象的引用。而不是clone的对象,这三个对象的email对象指向同一个地址。也就是所谓的浅克隆。

如果要实现深克隆,需要在克隆MyTestCloneAbnormal 类的时候,在MyTestCloneAbnormal类的clone处,克隆Email类。在Email类中也实现clone方法。代码如下所示。

MyTestCloneAbnormal类的clone方法public Object clone() throws CloneNotSupportedException {        //返回clone的对象//        return (MyTestCloneAbnormal) super.clone();        MyTestCloneAbnormal newp = (MyTestCloneAbnormal) super.clone();        newp.email = (Email)email.clone();        return newp;    }Email的clone类@Overrideprotected Object clone() throws CloneNotSupportedException {    return (Email) super.clone();}

结果如下所示:

[ yu ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ yu ]的邮件: 9.6中午会议正文是:[yu,你需要早到半小时][ 张三 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会][ 李四 ]的邮件: 9.6中午会议正文是:[今天中午十二点,大家在3楼会议室开会]

这就说明,我们实现了上面三个对象的Email对象深克隆,三个对象的Email对象都是指向了不同的内存地址,所以说,上面我们对p的改动才没有影响到其他两个p1,p2对象的Email属性。

其实出现问题的关键就在于clone()方法上,我们知道该clone()方法是使用Object类的clone()方法,但是该方法存在一个缺陷,它并不会将对象的所有属性全部拷贝过来,而是有选择性的拷贝,基本规则如下:

1、 基本类型   如果变量是基本很类型,则拷贝其值,比如int、float等。2、 对象   如果变量是一个实例对象,则拷贝其地址引用,也就是说此时新对象与原来对象是公用该实例变量。3String字符串   若变量为String字符串,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有紫都城对象保持不变。

还有一种通过字节流拷贝的方式,有兴趣可以自己去搜下。

参考的文章列表
http://blog.csdn.net/chenssy/article/details/12952063(他贴的代码有点问题,其他的还ok)
http://blog.csdn.net/zhangjg_blog/article/details/18369201/

原创粉丝点击