【java】克隆(clone),浅拷贝和深拷贝的区别

前言

在很多人初学的时候,一定会被克隆是什么?怎么使用克隆?浅拷贝和深拷贝又是啥?所搞得晕头转向。这里我希望用最简单的语言向大家介绍我自己的理解。(我们马克思老师说,你能不能用农民也听得懂的语言介绍一下这个知识。)

克隆

首先我们需要介绍一下克隆(clone)。在介绍克隆之前,请回忆一下一个包含对象引用的变量建立副本时会发生什么。代码如下图所示:在下面的代码中,new Employee()构造了一个新的对象,并返回一个引用的值赋值给了original。 然后把original的值又赋值给了copy。换句话说,现在copy和original都引用了同一个对象。(关于这部分不懂的同学,可以看我之前的博文http://t.csdn.cn/H468S

class Employee{
    String name;
    int age;
}

public class Test4 {
   public static void main(String[] args)  {
        Employee original = new Employee();
        Employee copy = original();
    }
}

上述代码中,我们可以看到,original 和 copy 同时引用了Employee对象。当我们通过copy修改某个成员变量的值的时候,原本Employee对象中的值也会得到修改。但是!在日常生活中,我们希望copy一个新对象,希望通过引用修改新对象的值不会造成原对象的值发生变化。 因此在这种情况下,就要用到克隆方法(clone)了。也就是说,我们希望clone后的结果如下图所示。

克隆的实现

接下来的问题就是怎么在代码中实现这个克隆。有的老哥说,既然克隆有方法,我直接一勺三花淡奶,在对象后面一个点克隆不就完了。

 显然这样的操作是不行滴。为啥呢? 在这里有一个前提,你要克隆某个类中的对象,这个类必须是可克隆的。那咋办?我们需要给Employee这个类实现一个Cloneable接口才行。如下图所示

( ′◔ ‸◔`)为啥还是不行?我们点进去这个Cloneable看看。

 原来文件告诉我们,要实现这个克隆方法,我们必须重写Cloneable这个接口中的clone方法。结果如下图所示。(在main函数后面添加的throws CloneNotSupportedException友友们可以暂时不用理解,先加上就好)

 ヾ(。`Д´。)但还是报错了,是什么情况?!注意看clone()方法的返回值是Object类,而object类是所有类父类。把Object类赋值给了Employee的copy引用发生了向下转型,因此这个时候必须要把Object类强制类型转换为Employee类。最终代码如下。

class Employee implements Cloneable {
    String name;
    int age;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Test4 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee original = new Employee();
        Employee copy = (Employee) original.clone();
    }
}

 通过调试发现,现在original和copy引用了不同的Employee()对象,从而实现了我们期望实现的clone的需求。

 浅拷贝

那么什么是浅拷贝呢?先说结论。clone默认实现的为浅拷贝。如果A拷贝了B,改变其中的一个对象的值,另一个对象的值会发生变化则是浅拷贝。而如果A拷贝了B,改变其中的一个对象的值,另一个对象的值不会发生任何变化则是深拷贝

先看如下代码和结果

 你可能会说:博主你骗人!这不是改变了一个对象的值,另一个不是没变化吗?这实现的不已经是深拷贝了吗?其实更加具体的浅拷贝说法是

如果 对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题、 但是如果对象包 含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,原对象和克隆的对象仍然会共享一些信息。

因此对于浅拷贝来说,如果是数值或其他基本类型,那么浅拷贝一点毛病没有。但是如果引用了其他对象,那么浅拷贝就很危险了。看如下代码: 这里我新建了一个Money类,然后在Employee中新建了一个Money类的对象。

 

结果发现:改变了其中copy中m中money的值,original和copy居然都发生了变化!!这就是浅拷贝所带来的危险!!!!其中的内存布局图如下

可见copy虽然克隆了original的对象,但是其中m的对象的引用的值也被克隆了。最终导致两个克隆的对象,仍然共享了 new Money()这块空间。因此通过m引用修改money的值,当然会导致两个引用均发生变化。 

浅拷贝的代码实现


class Money {
    public double money = 100.2;
}


class Employee implements Cloneable {
    String name;
    int age;

    public Money m = new Money();

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class Test4 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee original = new Employee("abc",15);
        Employee copy = (Employee) original.clone();
        System.out.println(original.m.money);
        System.out.println(copy.m.money);
        System.out.println("================");
        copy.m.money = 99;
        System.out.println(original.m.money);
        System.out.println(copy.m.money);
    }
}

 

 深拷贝

如上所说,而如果A拷贝了B,改变其中的一个对象的值,另一个对象的值不会发生任何变化则是深拷贝

 接上文所说,我希望的深拷贝后内存的布局应该如下所示。那么在代码层面如何实现呢?

 深拷贝的实现

既然我们要克隆Money以实现深拷贝,那就让他实现Cloneable接口,并重写方法

 

当然还不够。 必须要修改Employee类中的clone方法,以实现employee对象和money对象的拷贝。因此代码如下:

深拷贝整体代码如下 :


class Money implements Cloneable{  //既然我们要克隆Money以实现深拷贝,那就让他实现Cloneable接口,并重写方法

    public double money = 100.2;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


class Employee implements Cloneable {
    String name;
    int age;

    public Money m = new Money();

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
       Employee employee = (Employee) super.clone(); //此代码只是克隆了Employee对象
        employee.m = (Money) this.m.clone();  //此代码实现了Employee对象中Money对象的克隆。
        return employee;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class Test4 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee original = new Employee("abc",15);
        Employee copy = (Employee) original.clone();
        System.out.println(original.m.money);
        System.out.println(copy.m.money);
        System.out.println("================");
        copy.m.money = 99;
        System.out.println(original.m.money);
        System.out.println(copy.m.money);
    }
}

实验结果如下:

 

可以看到,我们修改一个对象的值,并不会影响另一个对象的值,这就完成了深拷贝了!!!

 

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值