刨析Java中浅拷贝和深拷贝

一、前言

拷贝的目的就是为了获得相同的对象,当需要属性值相同的对象时,不需要再重新一步一步创建

在Java中的拷贝需要实现Clonable接口,重写Object的clone()方法;同时在Java中的拷贝也分为浅拷贝和深拷贝,其两者的区别就在于对 对象中引用数据类型的不同处理方法。即:

浅拷贝就是克隆出来的新对象与原对象完全一致无二,包括引用类型字段的值。这就会有一个现象,针对引用类型的字段,两个对象的引用地址一致,如此一来新旧对象之间强关联,修改其中一个对象的内容,极可能影响到另外一个的内容。

 深拷贝是针对浅拷贝而言的,那就是将所有引用的对象同样做深拷贝,这样一来,新旧对象就不再强关联,当我们修改其中一个对象时,不会影响到另外一个。

二、浅拷贝

(一)概念

基本数据类型拷贝值,引用类型拷贝内存地址,修改基本数据类型不会对原对象产生影响,修改引用类型会对原对象产生影响。被拷贝对象通过实现 Cloneable 接口并 重写clone() 方法来实现浅拷贝。

(二)代码说明

 public class Person implements Cloneable{
    private String name;
    private Integer age;
    public String getName(){
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }


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

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
}



     public static void test01() throws CloneNotSupportedException {
        Person p1=new Person();
        p1.setName("小王");
        p1.setAge(18);
        Person p2=p1.clone();
        System.out.println(p1+":"+p1.hashCode());
        System.out.println(p2+":"+p2.hashCode());
    }



运行结果:
Person{name='小王', age=18}:1324119927
Person{name='小王', age=18}:1915910607

通过上述代码 我们可以看出拷贝出的Person对象的存储哈希表上的哈希值不一样,说明两个对象的内存位置不同,即成功实现了对象的拷贝。下面的例子将进一步说明成员中引用类型哈希值的变化

Children类:实现Clonable接口

public class Children implements Cloneable{
    private String name;
    private Integer age;
    public String getName(){
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

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


}

 Person类 其中存在Children类型的成员变量--->即引用类型变量

public class Person implements Cloneable{
    private String name;
    private Integer age;
    private Children child;
    public String getName(){
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setChild(Children child) {
        this.child = child;
    }

    public Children getChild() {
        return child;
    }

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

    @Override
    public Person clone() throws CloneNotSupportedException {
//        return (Person) super.clone();
        Person clone=(Person)super.clone();//克隆Person对象
        return clone;
    }
}


//以下为测试类

 public static void test02() throws CloneNotSupportedException {
        Person p1=new Person();
        p1.setName("小王");
        p1.setAge(28);
        Children children1=new Children();
        children1.setName("张伟");
        children1.setAge(5);

        p1.setChild(children1);

        Person p2=p1.clone();
        System.out.println(p1+":对象的哈希值"+p1.hashCode()+":成员变量的哈希值"+p1.getChild().hashCode());
        System.out.println(p2+":对象的哈希值"+p2.hashCode()+":成员变量的哈希值"+p1.getChild().hashCode());
    }

 测试结果:

Person{name='小王', age=28}:对象的哈希值990368553:成员变量的哈希值1096979270
Person{name='小王', age=28}:对象的哈希值2093176254:成员变量的哈希值1096979270

说明 浅拷贝的对象占用不同的内存位置,但引用类型的成员 哈希值一样 ,则说明两对象之间的变量会相互影响。任一个变量的变化会影响到另一个变量。

通过上述的实例,我们归纳以下步骤:

  • 被拷贝对象的类实现Cloneable接口
  • 被拷贝对象的类重写Object的clone方法
  • 执行拷贝操作,就是调用重写的clone方法来完成克隆

三、深拷贝

(一)概念

深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。基础数据类型或者引用类型,对其中一个对象修改该值,不会影响另外一个。每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。

 (二)代码说明

对Children类重写clone()方法:

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

对Person重写clone()方法:

   @Override
    public Person clone() throws CloneNotSupportedException {
//        return (Person) super.clone();
        Person clone=(Person)super.clone();//克隆Person对象
        clone.setChild(child.clone()); //克隆Person中的成员变量 通过成员变量child的副本传参
        return clone;
    }

测试类:

 public static void test04() throws CloneNotSupportedException {
        Person p1=new Person();
        p1.setName("小王");
        p1.setAge(28);
        Children children1=new Children();
        children1.setName("张伟");
        children1.setAge(5);
        p1.setChild(children1);

        Person p2=p1.clone(); //克隆person对象 同时也对Children成员变量进行了克隆 即两者完全剥离

        System.out.println(p1.getChild());
        System.out.println(p2.getChild());

        children1.setName("张三");//单独设置children1的属性
        System.out.println(p1.getChild());
        System.out.println(p2.getChild());

        Children children2=p2.getChild();//此时p2为p1的副本 ,用p2调用getChild方法来获取children对象
        children2.setName("张无忌"); //单独设置children2的属性
        System.out.println(p1.getChild());
        System.out.println(p2.getChild());
        //打印出两个子对象的哈希值 两者哈希值不一样说明 两个对象的存储位置不一样
        System.out.println(p1.getChild().hashCode());
        System.out.println(p2.getChild().hashCode());


    }
Children{name='张伟', age=5}
Children{name='张伟', age=5}
Children{name='张三', age=5}
Children{name='张伟', age=5}
Children{name='张三', age=5}
Children{name='张无忌', age=5}
668386784
1329552164

从上述代码可以分析出,对对象进行深拷贝后,改变对象中引用类型数据的值 不会影响到另一个对象,通过最后两行的哈希值也可以比较出 两个子对象的哈希值是不相同的,这样就实现了对象间关系的完全脱离,以免对其他对象的内容造成影响。

深拷贝步骤:

先Children类实现接口并重新clone方法 返回本类的副本

后Person类重写clone方法,先克隆本类对象 ,再通过克隆的本类对象调用setChildren()方法传入克隆的children类,实现层层拷贝。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pursuit@H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值