1
什么是
Clone
,容易实现吗?
简单地说,
Clone
就是对于给定的一个对象实例
o
,得到另一个对象实例
o’
:
o
与
o’
类
型相同(
o.getClass() == o’.getClass()
),内容相同(对于
o/o’
中的字段
f
,如果
f
是基本数据类型,则
o.f == o’.f
;如果
f
是对象引用,则
o.f == o’.f
或
o.f
指向的对象与
o’.f
指向的对象的内容相同)。通常称
o’
为
o
的克隆或副本。
直观上看,似乎很容易为一个类加上
clone
方法:
class A {
private Type1 field1;
private Type2 field2;
…..
public Object clone() {
A a = new A();
a.field1 = a.getField1();
a.field2 = a.getField2();
……
return a;
}
}
|
然而,稍加推敲,就会发现这样的实现方法有两个问题:
1.
要想某个类有
clone
功能,必须单独为其实现
clone()
函数,函数实现代码与该类定义密切相关。
2.
即使基类
A
已有
clone()
函数,其子类
ExtendA
若要具备
clone
功能,则必须
override
其基类
A
的
clone()
函数。否则,对类型为
ExtendA
的对象
ea
的
clone()
方法的调用,会执行于类
A
中定义的
clone()
方法而返回一个类型为
A
的对象,它显然不是
ea
的克隆。
2
Java
对
clone
的支持
万类之初的
Object
类有
clone()
方法:
protected native Object clone() throws CloneNotSupportedException;
|
该方法是
protected
的,显然是留待被子类
override
的。该方法又是
native
的,必然做了
与具体平台相关的底层工作。
事实上,类
Object
的
clone()
方法首先会检查
this.getClass()
是否实现了
Cloneable
接口。
Cloneable
只是一个标志接口而已,用来标志该类是否有克隆功能。
public
interface
Cloneable {
}
|
如果
this.getClass()
没有实现
Cloneable
接口,
clone()
就会抛
CloneNotSupportedException
返回。否则就会创建一个类型为
this.getClass()
的对象
other
,并将
this
各
field
的值赋值给
other
的对应
field
,然后返回
other
。
如此一来,我们要定义一个具有
Clone
功能的类就相当方便:
1.
在类的声明中加入“
implements Cloneable
”,标志该类有克隆功能;
2.
Override
类
Object
的
clone()
方法,在该方法中调用
super.clone()
:
class CloneableClass implements Cloneable {
……
public Object clone() {
try {
return super.clone();
//
直接让
Object.clone()
为我们代劳一切
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
}
|
3
Shallow Clone
与
Deep Clone
3.1
Shallow
与
Deep
从何而来
一个具有克隆功能的类,如果有可变(
Mutable
)类类型的字段
field
,如何为其克隆(副
本)对象
o’
中的
field
赋值?
方法一、如
Object
的
clone()
方法所实现:设原始对象为
o
,其克隆对象是
o’
,执行
o’.field = o.field
。这样,
o’.field
与
o.field
指向同一个可变对象
m
。
o
与
o’
可能会相互影响(一个对象的状态可能会随着另一个对象的状态的改变而改变)。这样的
Clone
称为
Shallow Clone
。这也是
Object
的
clone()
方法的实现方式。
方法二、将
o.field
指向的可变对象
m
克隆,得到
m’
,将
m’
的引用赋值给
o’.field
。这样
o’
与
o
内容相同,且相互之间无影响(一个对象状态的改变不会影响另一个对象的状态)。这样的
Clone
称为
Deep Clone
。
Java Collection
类库中具体数据结构类(
ArrayList/LinkedList
,
HashSet/TreeSet
,
HashMap/TreeMap
等)都具有克隆功能,且都是
Shallow Clone
,这样设计是合理的,因为它们不知道存放其中的每个数据对象是否也有克隆功能。
System.arrayCopy()
的实现采用的也是
Shallow Clone
。
Deep Clone
对于实现不可变(
Immutable
)类很有帮助。设一个类包含可变类
M
类型的
field
,如何将其设计为不可变类呢?先为
M
实现
Deep Clone
功能,然后这样设计类
ImmutableClass
:
class ImmutableClass {
MutableClass m;
ImmutableClass(MutableClass m) {
this.m = m.clone();
//
将传入的
m
的
clone
赋值给内部
m
}
public MutableClass getM() {
return this.m.clone();
//
将内部
m
的
clone
返回给外部
}
}
|
3.2
如何实现
Deep Clone
检查类有无可变类类型的字段。如果无,返回
super.clone()
即可;
如果有,确保包含的可变类本身都实现了
Deep Clone
;
Object o = super.clone();
//
先执行浅克隆,确保类型正确和基本类型及非可变类类型字段内容正确
对于每一个可变类类型的字段
field
:
o.field = this.getField().clone();
返回
o
。