java中的深浅克隆
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
java中的深浅克隆
假设有⼀个对象object,在某处⼜需要⼀个跟object⼀样的实例object2,强调的是object和object2是两个独⽴的实例,只是在开始的时候,他们是具有相同状态的(属性字段的值都相同)。
遇到这种情况的做法⼀般是,重新new⼀个对象object2,将object的字段值赋予object2,即:object2=object; 这样的话两个引⽤仍然指向的是同⼀个对象,不是两个对象。
克隆⽅法clone()
Java中跟克隆有关的两个类分别是Cloneable接⼝和Object类中的clone⽅法,通过两者的协作来实现克隆。
⾸先来看看Object的clone()源代码:
⾸先看⼀下java api doc中关于Cloneable接⼝和Object类中的clone⽅法的描述:
ng.Cloneable 接⼝(以下源引JavaTM 2 Platform Standard Ed. 5.0 API DOC)
此类实现了 Cloneable 接⼝,以指⽰ Object.clone() ⽅法可以合法地对该类实例进⾏按字段复制。
如果在没有实现 Cloneable 接⼝的实例上调⽤ Object 的 clone ⽅法,则会导致抛出 CloneNotSupportedException异常。
按照惯例,实现此接⼝的类应该使⽤公共⽅法重写 Object.clone(它是受保护的)。
请参阅 Object.clone(),以获得有关重写此⽅法的详细信息。
注意,此接⼝不包含 clone ⽅法。
因此,因为某个对象实现了此接⼝就克隆它是不可能的。
即使 clone ⽅法是反射性调⽤的,也⽆法保证它将获得成功。
Cloneable接⼝没有任何⽅法,仅是个标志接⼝(tagging interface),若要具有克隆能⼒,实现Cloneable接⼝的类必须重写从Object继承来的clone⽅法,并调⽤Object的clone⽅法(见下⾯Object#clone的定义),重写后的⽅法应为public 的。
clone⽅法⾸先会判对象是否实现了Cloneable接⼝,若⽆则抛出CloneNotSupportedException, 最后会调⽤internalClone. intervalClone是⼀个native⽅法,⼀般来说native⽅法的执⾏效率⾼于⾮native⽅法。
当某个类要复写clone⽅法时,要继承Cloneable接⼝。
通常的克隆对象都是通过super.clone()⽅法来克隆对象。
浅克隆(shadow clone)
克隆就是复制⼀个对象的复本,若只需要复制对象的字段值(对于基本数据类型,如:int,long,float等,则复制值;对于复合数据类型仅复制该字段值,如数组变量则复制地址,对于对象变量则复制对象的reference。
举个例⼦:
测试代码如下:
运⾏结果:
克隆前c1: a=100 b=1000
克隆前c1: a=100 b=5
克隆后c2: a=50 b[0]=5
c1和c2的对象模型:
可以看出,基本类型可以使⽤浅克隆,⽽对于引⽤类型,由于引⽤的是内容相同,所以改变c2实例对象中的属性就会影响到c1。
所以引⽤类型需要使⽤深克隆。
另外,在开发⼀个不可变类的时候,如果这个不可变类中成员有引⽤类型,则就需要通过深克隆来达到不可变的⽬的。
深克隆(deep clone)
深克隆与浅克隆的区别在于对复合数据类型的复制。
若对象中的某个字段为复合类型,在克隆对象的时候,需要为该字段重新创建⼀个对象。
再举⼀个例⼦:
运⾏结果:
克隆前c1: a=100 b=1000
克隆前c1: a=100 b=1000
克隆后c2: a=50 b[0]=5
对象模型:
使⽤序列化实现深克隆
然后编写测试类:
运⾏后的结果如下:
克隆前dc1: a=100 b[0]=1000
克隆后dc1: a=100 b[0]=1000
克隆后dc2: a=50 b[0]=500
可以看到,两个引⽤所指向的对象在堆中相互独⽴,互不⼲扰,这样就实现了深度克隆。
总结:
1、克隆⽅法⽤于创建对象的拷贝,为了使⽤clone⽅法,类必须实现ng.Cloneable接⼝重写protected⽅法clone,如果没有实现Clonebale接⼝会抛出CloneNotSupportedException.
2、在克隆java对象的时候不会调⽤构造器
3、java提供⼀种叫浅拷贝(shallow copy)的默认⽅式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含引⽤类型,那么原始对象和克隆都将指向相同的引⽤内容,这是很危险的,因为发⽣在可变的字段上任何改变将反应到他们所引⽤的共同内容上。
为了避免这种情况,需要对引⽤的内容进⾏深度克隆。
4、按照约定,实例的克隆应该通过调⽤super.clone()获取,这样有助克隆对象的不变性。
如:clone!=original和
clone.getClass()==original.getClass(),尽管这些不是必须的
参考资料。