java 深克隆及浅克隆

我继续在看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@2a84aee7
test.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@2a84aee7
test.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类

@Override
protected 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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值