Java 浅拷贝和深拷贝

浅拷贝和深拷贝

浅拷贝:直接复制了对象A地址给另一个对象B。由于对象A,B指向的是同一个地址因此修改B,A也会跟着改变。
在这里插入图片描述
深拷贝:复制了对象A的值给另一个对象B,这两个对象没有任何关系指向不同的地址。修改其中一个并不会对另一个造成影响。
在这里插入图片描述

浅拷贝

先把要用到的两个类放上来,一个狗类,一个毛类get()、set()什么的就不放了。

class Dog{
    private String name;
    private int age;
    private String color;
    private Mao mao;
    }
class Mao {
    private String changduan;
    private String color;
    }

直接赋值

public void assign() {
		Dog d1 = new Dog("d1", 1, "green");
        Dog d2 = d1;
        d2.setName("d2");
        // 可以看到修改了d2后,d1也改变了
        System.out.println("d1 name:" + d1.getName());
        System.out.println("d2 name:" + d2.getName());
    }

输出:
     d1 name:d2
     d2 name:d2
     可以看到修改了d2的name后,d1的name也发生了改变

重写clone()

clone()其实可以说是半深半浅。下面细🔒
可以通过重写Object类的clone()方法来进行拷贝。

clone的深拷贝方面

首先要重写clone()方法需要实现Cloneable接口

class Dog implements Cloneable{
    private String name;
    private int age;
    private String color;
    private Mao mao;
     /**
     * 重写clone方法
     */
    @Override
    public Dog clone() {
        try {
            return (Dog)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
    }

然后创建一个一岁长毛狗d1,并使用clone()方法将d1拷贝给d3

  public void clone_() {
      Dog d1 = new Dog("d1", 1, "green");
      Mao mao =new Mao("long","black");
      d1.setMao(mao);
      Dog d3 = d1.clone();
      d3.setAge(2);
      System.out.println("d1:的年龄" + d1.getAge());
      System.out.println("d3:的年龄" + d3.getAge());
    }

输出:
     d1:的年龄1
     d3:的年龄2
     我们发现修改了d3的age后并没有影响到d1。这就是深拷贝。

clone的浅拷贝方面

还是之前那两个对象,只不过这次我们修改其中的Mao(毛) 属性。

    public void clone_() {
        Dog d1 = new Dog("d1", 1, "green");
        Mao mao =new Mao("long","black");
        d1.setMao(mao);
        Dog d3 = d1.clone();
        d3.setAge(2);
        mao.setChangduan("short");
        mao.setColor("red");
        System.out.println("d1的Mao属性" + d1.getMao()+"d1:的年龄" + d1.getAge());
        System.out.println("d3的Mao属性" + d3.getMao()+"d3:的年龄" + d3.getAge());
    }

输出:
     d1的Mao属性Mao [changduan=short, color=red]d1:的年龄1
     d3的Mao属性Mao [changduan=short, color=red]d3:的年龄2
     我们发现修改了修改d3age年龄属性不会对d1造成影响,但是修改d3Mao属性就会把d1Mao属性也改变了。这就是clone()浅拷贝!

为啥又有深又有浅

如下图所示,虽然重写了clone()方法,拷贝出来的对象指向了不同的地址。但是d1d3Mao属性并没被拷贝出来。也就是说d1d3的属性Mao指向的是同一个地址,所以修改Mao的值,就会同时影响到d1d3
在这里插入图片描述

总结

可以使用clone()直接深拷贝的:

  1. 本数据类型如intchar
  2. 基本数据类型的包装类Integer
  3. String

不可以简单的使用clone()直接深拷贝的:
引用类型的数据如我上面自己定义的Mao类,数组等。
但是重写clone()的时候处理下还是可以深拷贝的

深拷贝

重写clone()

没错又是重写clone(),只要我们对对象中的引用类型进行处理,就可以使用clone进行深拷贝。就是让这个引用类型也重写clone(套娃!)
首先让Mao类也重写clone(),并修改Dog类的clone()逻辑,把拷贝后的Mao赋值给拷贝后的Dog

class Mao implements Cloneable{
    private String changduan;
    private String color;
    }
    @Override
    public Mao clone()  {
        try {
            return (Mao)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
class Dog implements Cloneable{
    private String name;
    private int age;
    private String color;
    private Mao mao;
     /**
     * 重写clone方法
     */
 	@Override
    public Dog clone() {
        try {
            Dog res=(Dog)super.clone();
            res.mao=mao.clone();
            return res;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

然后使用clone()进行拷贝

public void deepClone() {
        Mao mao = new Mao("long", "black");
        d1.setMao(mao);
        Dog d3 = d1.clone();
        d3.setName("d3");
        mao.setChangduan("short");
        mao.setColor("red");
        System.out.println("d1:" + d1);
        System.out.println("d3:" + d3);
    }

d1:Dog [age=1, color=green, mao=Mao [changduan=short, color=red], name=d1]
d3:Dog [age=1, color=green, mao=Mao [changduan=long, color=black], name=d3]
但是如果Mao类里面又有一个引用类型的属性,那就又要继续重写clone()无限套娃很麻烦。
完美实现深拷贝

new一个新对象

我们可以通过new一个新的对象,然后把老对象的值复制给新对象来做到深拷贝(对象较复杂的时候很麻烦

   public void newObj() {
        Dog d2 = new Dog(d1.getName(), d1.getAge(), d1.getColor());
        d2.setAge(88);
        System.out.println("d1:" + d1);
        System.out.println("d2:" + d2);
    }

老简单了,不多说了

使用序列化和反序列化进行深拷贝

序列化可以把java类的所有数据及其数据类型都输出到IO中,然后反序列化就是从IO中读取这些数据重新生成对象。由于是直接读取值然后新生成一个对象,所以就是深拷贝

    public void serDeepCopy() throws IOException, ClassNotFoundException {
        Mao mao=new Mao("short", "red");
        d1.setMao(mao);
        //先把序列化数据放到内存操作流buf中
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        ObjectOutputStream obs = new ObjectOutputStream(buf);
        obs.writeObject(d1);
        obs.close();
        //从buf流中读取数据 并反序列化
        ByteArrayInputStream ios = new ByteArrayInputStream(buf.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(ios);
        Dog d4=(Dog)ois.readObject();
        mao.setColor("black");
        d4.setName("d4");
        System.out.println("d1:" + d1);
        System.out.println("d4:" + d4);
    }

d1:Dog [age=1, color=green, mao=Mao [changduan=short, color=black], name=d1]
d4:Dog [age=1, color=green, mao=Mao [changduan=short, color=red], name=d4]

完整代码地址(点个star吧 ball ball了)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值