Java中的浅拷贝与深拷贝



Java中的对象都有一个方法叫clone(),是从Object类中继承而来的,它的作用是复制一个对象并返回,除了在堆中的地址不一样以外,对象里的实例变量、方法都与原对象完全一致。调用该方法前必须先重写clone(),而重写则需要先实现Cloneable接口,直接重写会抛出CloneNotSupportedException异常。下面举个例子:

class person implements Cloneable{

      private int age;

      private Stringname;

      private hat hat;

    

      person(int age,String name,hat hat){

        this.age = age;

        this.name = name;

        this.hat = hat;

      }

  

      public int getAge() {

        return this.age;

      }

  

      public void setAge(int age) {

        this.age = age;

      }    

 

public String getName() {

           return this.name;

      }

  

      public void setName(String name) {

        this.name = name;

      }

  

      public hat getHat() {

        return this.hat;

      }

  

      public void setHat(hat h) {

        this.hat = h;

      }

  

      public person clone() throws CloneNotSupportedException {

        return (person)super.clone();

      }

}

class hat{

      private Stringcolor;

  

      hat(Stringcolor){

        this.color = color;

      }

  

      public void setColor(String color) {

        this.color = color;

      }

  

      public String getColor() {

        return this.color;

      }


}

这是一个很简单的类,但举例完全足够了。类里面有一个int表示年龄,一个String表示名字,有一个hat类表示各种颜色的帽子,另外提供了setget方法。重点是person类现在已经实现了cloneable接口,可以重写clone()了,重写也非常简单,只需要直接调用父类的方法即可返回克隆对象,这时需要捕捉或者抛出CloneNotSupportedException异常。

下面是一些测试:

personp1 = new person(18,"xiaomi",new hat("black"));

personp2 = p1.clone();

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

System.out.println(p1.getAge()==p2.getAge());

System.out.println(p1.getName()==p2.getName());

System.out.println(p1.getHat()==p2.getHat());

这是输出结果:

false

true

true

true

可以看到两个对象除了在堆中的地址不一样以外,其实例变量和方法都完全一致。但这只是一种浅拷贝,我们来看下面的测试:

p1.getHat().setColor("pink");

System.out.println(p1.getHat().getColor());

System.out.println(p2.getHat().getColor());

测试结果:

pink

pink

p1中的hat对象修改成pink后,p2hat对象也同时被修改了,这显然不是我们想要的结果。如果细心看上面的代码后就会发现,clone()返回的克隆对象中对象引用的都是同一个对象,如果我们通过引用直接修改对象,就相当于修改持有所有浅拷贝得来的对象中的对象引用。如果要改变这一种情况,则需要深拷贝。

如果你已经看懂了上面的浅拷贝,那么深拷贝其实很好理解,就是将类中的所有对象引用的类都实现浅拷贝。Person类中有一个hat类(String是一个不可变对象,任何对String的修改都会重新获得一个新的String对象,在这里我们可以将它视为基本类型),那么我们只需要将hat类也拷贝一份并将克隆对象的引用修改后再返回即可。代码如下:

publicObject clone() throws CloneNotSupportedException {

     person newPerson = (person) super.clone();

     newPerson.hat = hat.clone();

     return newPerson;

}

要调用hatclone(),那么首先hat也必须实现Cloneable接口并重写clone()。代码如下:

public hatclone() throws CloneNotSupportedException {

     return (hat) super.clone();

   }

我们来测试一下:

p1.getHat().setColor("pink");

System.out.println(p1.getHat().getColor());

System.out.println(p2.getHat().getColor());

测试结果:

pink

black

现在已经完全符合我们的需求了,但是还有一个问题就是每次深拷贝的时候,要求将类中的所有对象引用的类都实现Cloneable接口并重写Clone(),在非常大的工程项目中,引用关系变得十分复杂,如果需要实现对一个对象Clone会变得异常繁琐甚至是无法做到,但如果不使用深拷贝则会在某些情况下由于引用关系出错,因此sun也将该方法隐藏起来,我们使用clone()前一定要谨慎使用,并且根据实际场景选择性地使用这两种不同的拷贝方式。

如果你有更好的方法欢迎在评论区留言,欢迎点赞转发,感谢阅读。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值