深拷贝(clone)、浅拷贝

深拷贝(clone)、浅拷贝

简易理解:

浅拷贝:只拷贝源对象的地址,所以源对象的任何值变化,拷贝对象值也随着变化。

深拷贝:拷贝对象的值,而不是地址,当源对象的值发生变化,拷贝对象的值不会发生变化

浅拷贝

例子展示:

先建立一个对象
在这里插入图片描述
在这里插入图片描述
可以看见,浅拷贝改变源对象的值,拷贝对象值也会发生改变

深拷贝

为啥我们需要深拷贝呢?在某些业务场景下,我们需要完全相同但是缺不相关联的对象。其实大体有两种,Serializable与Cloneable

深拷贝的几种方式:

  1. 构造函数方式
  2. 重写clone方法
  3. Apache Commons Lang 序列化
  4. Gson序列化
  5. Jackson序列化

构造函数完成深拷贝

在这里插入图片描述
这是通过new的方式,对系统的开销比较大。它俩是隔离的

重写clone()方法

它是Object中的。这是一个native方法,即用非java语言编写的,从一方面来将它的效率更高。

在这里插入图片描述
可以看见它是 protected修饰的,该修饰符的访问范围。复习一下

访问级别访问控制符同类同包子类不同包
公开publictruetruetrueture
受保护protectedtruetruetruefalse
默认没有修饰符truetruefalsefalse
私有privatetruefalsefalsefalse

所以要想克隆对象,要实现Cloneable接口。即告诉虚拟机我们可以利用这个对象完成深拷贝
在这里插入图片描述
可以看见它是没有任何抽象方法的

在这里插入图片描述
可以看见,我们现在还不能clone,还需要重写方法。因为Cloneable它仅仅是一个标志,你可以看见,它里面是空的。

来实现Object的clone方法
在这里插入图片描述

但是,我们还需要修改三个地方。访问修饰符和返回对象的类型
在这里插入图片描述

protected解释,例子
  • 这里访问修饰符由 procted改为了 public。这里复习一下java基础:子类重写父类的方法时访问修饰符要大于等于父类的,否则就破坏了继承规则:本来父类的某个方法是可以访问的,到了子类这里就不可以访问了。

具体一点,procted是访问修饰符里面最复杂的

准备一个 java0包中的A03,A02,java包中的A01与A02

image-20200609124919263

在同一个 包中:
  1. 在(子类[允许被继承]、父类)可直接使用

子类:

image-20200609125336319

子类的mian方法也可以访问

image-20200609131751536

父类:
image-20200609125401692

  1. 子类外,可以通过(子类、父类)【对象 .】

image-20200609125806052

不在同一个包中:
  1. 当前类外,不能使用 **【对象 .】**的方式。

image-20200609124734019

可见到没有 A03 类中的 a03 成员

  1. 子类中,可继承protected成员,且在非main方法中可直接使用名称进行访问。在子类的main方法中可(子类) **【对象.】**调用,但是不能(父类)调用,这一点有点像 序号1的内容(相当于父类跨包)。

image-20200609132039340

  1. 子类外,与子类同包。通过子类打点方式调用这个方式调用protected是不行的,更别说父类了。需要重写这个成员或方法。

image-20200609132728703

来重新加上,这样肯定就可以了,相当于之前的同包

image-20200609132805755

image-20200609132857896

4.在使用子类的类中,与父类同包,(子类,父类)都可以通过打点的方式获得

image-20200609133128516

以上结果需把之前重写的注释掉

image-20200609133236547

当然这是把A02之前覆盖A03的procted删去了的,不然又是不同包访问了某个类类中的procted了,会报错。这是不同包中 1 的结论

为什么colone要重写且改成public
  1. 与子类与父类都不同包,若想通过子类访问父类的procted,则需要重写该成员并将访问修饰符改为 public!!!!!说了那么多,其实这才是跟我们要讲的clone相关的,前面只是带大家复习一下

    为什么呢?前面不是说了吗,不同包不能打点使用procted,那就只能比他上一级咯,private->default->procted->public。但是clone()是Object的,我们继承过来的,想想 我们上面的例子 **不同包中的第三点,与子类同包,与子类不同类调用子类打点的方式需要重写这个protected。因为不然相当于父类的成员跨包,**现在在前面的基础上改成了与子类不同包了,类比一下肯定是要重写方法的,好像感觉难度也增加了。那么现在有第三个包了,我们不可能在第三个包又加这个子类吧,所以我们就可以把子类重写的procted访问修饰符改为public就可以了。

    当然这个可以帮助我们达到第三个包访问的目的,但是访问修饰符改变了,这个需要我们自己去权衡。

    在开发中这种情况很常见。往往我们的一个对象(默认继承了java.lang.Object)在一个entity包下,然后又在另一个包下使用这个bean,这样就是三个包了

示例:(三个不同包下)
image-20200609135444883

image-20200609135503806

会报错,此时将A02中的protected改为public。结果显而易见

image-20200609135546245

clone()方法

在这里插入图片描述

但是,我们发现地址不一样。成功。看起来比两个new简洁。

如果Person有多个属性,通过new的方式看起来是不是很恶心:
在这里插入图片描述

后期修改就比较麻烦。

但是呢,它不能完成成员中有对象的clone除了【String】,否则对象就是浅拷贝,这是他的弊端就凸显出来了。下面举例:

当clone()对象的成员中存在一个对象时

准备A02【包含对象A01】:

image-20200610100800469

A01:

image-20200610100814682

结果:

image-20200610100929148

可以发现,clone是clone了,但是里面的对象是浅拷贝。修改clone的也修改了原来的。那么在这种情况下怎么进行深拷贝呢?

那么我们想想是不是应该把A01也就是它的这个成员对象也变为clone呢?试一试。

image-20200610101414241

可以看见,还是不行。类比这么想一下,我们之前A02重写了clone方法,在Test中克隆的,显式调用了clone,所以才深拷贝了A02。

现在A02中有A01对象了,是也要对A01进行深拷贝的,是不是也该在A02中的clone方法显式调用一下呢?

image-20200610112920610

结果:

image-20200610112953175

至于为什么这么做,我还不能做出很好的解释[之后补上]。不过通过这样的例子你应该可以很好地快速记忆,知道如何使用了。

那么如果没有实现Cloneable接口,重写了clone方法呢?

image-20200608211619116

会报 CloneNotSupportedException异常

如果没重写clone()方法,实现了Cloneable接口呢?。。。那肯定没clone方法啊。其实我还是觉得clone()比较麻烦,使用起来要多加小心,例如你必须对所有的对象都要有所了解。那么有没有其他方式呢?其实也就是Serializable,现在有很多框架都对序列化进行了封装,方便我们使用。

如果对象很复杂,多个继承怎么办,用clone就比较麻烦了。

可以考虑通过序列化的方式。

本篇文章不讲框架的使用,主要是帮助大家理解深拷贝是什么东西?

至于序列化与框架使用,之后章节详细讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值