克隆就是复制一个对象的复本.但一个对象中可能有基本数据类型,如:int,long,float 等,也同时含有非基本数据类型如(数组,集合等)
被克隆得到的对象基本类型的值修改了,原对象的值不会改变.这种适合shadow clone(浅克隆).
但如果你要改变一个非基本类型的值时,原对象的值却改变了,.比如一个数组,内存中只copy他的地址,而这个地址指向的值并没有copy,当clone时,两个地址指向了一个值,这样一旦这个值改变了,原来的值当然也变了,因为他们共用一个值.,这就必须得用深克隆(deep clone)
以下举个例子,说明以上情况:
被克隆类:ShadowClone.java
class
ShallowClone
implements
Cloneable {
public
int
a;
public
int
[] b;
public
ShallowClone() {
a
=
5
;
b
=
new
int
[] {
1
,
2
,
3
,
4
,
5
};
}
//
浅克隆,对于克隆后的对象,只能保证对基础类型成员的修改不会影响原对象的相应成员
//
对类类型和数组类型的成员,只是拷贝了对象的地址,因此对克隆后对象的这些类型成员
//
进行修改会影响原对象
@Override
public
Object clone() {
ShallowClone sc
=
null
;
try
{
sc
=
(ShallowClone)
super
.clone();
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
return
sc;
}
}
测试类:
public
class
DeepAndShallowClone {
public
static
void
main(String[] args)
throws
Exception {
//
shallow clone
ShallowClone sc
=
new
ShallowClone();
ShallowClone scCopy
=
(ShallowClone)sc.clone();
System.out.println(
"
Shallow Copy
"
);
System.out.println(
"
--Before clone
"
);
System.out.println(
"
sc.a=
"
+
sc.a);
System.out.println(
"
sc.b=
"
+
sc.b[
0
]);
scCopy.a
=
1
;
scCopy.b[
0
]
=
10
;
System.out.println(
"
--After clone
"
);
System.out.println(
"
sc.a=
"
+
sc.a);
System.out.println(
"
sc.b=
"
+
sc.b[
0
]);
System.out.println(
"
scCopy.a=
"
+
scCopy.a);
System.out.println(
"
scCopy.b=
"
+
scCopy.b[
0
]);
}
}
结果如下:
Shallow Copy
--
Before clone
sc.a
=
5
sc.b
=
1
--
After clone
sc.a
=
5
sc.b
=
10
scCopy.a
=
1
scCopy.b
=
10
问题出现了,修改了克隆后的对象scCopy.b[0]的值,但sc.b[0]的值也改变了,与scCopy.b[0]的值相等.
以下针对浅克隆得出结论:基本类型是可以被克隆的,但引用类型只是copy地址,并没有copy这个地址指向的对象的值,这使得两个地址指向同一值,修改其中一个,当然另一个也就变了.
由此可见,浅克隆只适合克隆基本类型,对于引用类型就不能实现克隆了.
那如何实现克隆引用对象呢,以下提供一种方法. 用序列化与反序列化实现深克隆(deep copy)
被克隆对象.DeepClone.java
class
DeepClone
implements
Serializable {
private
static
final
long
serialVersionUID
=
1L
;
public
int
a;
public
int
[] b;
public
DeepClone() {
a
=
10
;
b
=
new
int
[] {
6
,
7
,
8
,
9
,
10
};
}
//
使用ObjectInput(Output)Stream和ByteArrayInput(Output)Stream实现深克隆
public
Object deepClone()
throws
IOException, ClassNotFoundException {
DeepClone dc
=
null
;
ByteArrayOutputStream baos
=
new
ByteArrayOutputStream();
ObjectOutputStream oos
=
new
ObjectOutputStream(baos);
oos.writeObject(
this
);
oos.close();
ByteArrayInputStream bais
=
new
ByteArrayInputStream(baos.toByteArray());
ObjectInputStream bis
=
new
ObjectInputStream(bais);
dc
=
(DeepClone)bis.readObject();
return
dc;
}
}
测试类:
public
class
DeepAndShallowClone {
public
static
void
main(String[] args)
throws
Exception {
DeepClone dc
=
new
DeepClone();
DeepClone dcCopy
=
(DeepClone)dc.deepClone();
System.out.println(
"
--Before clone
"
);
System.out.println(
"
dc.a=
"
+
dc.a);
System.out.println(
"
dc.b=
"
+
dc.b[
0
]);
dcCopy.a
=
1
;
dcCopy.b[
0
]
=
1
;
System.out.println(
"
Shallow Copy
"
);
System.out.println(
"
--After clone
"
);
System.out.println(
"
dc.a=
"
+
dc.a);
System.out.println(
"
dc.b=
"
+
dc.b[
0
]);
System.out.println(
"
dcCopy.a=
"
+
dcCopy.a);
System.out.println(
"
dcCopy.b=
"
+
dcCopy.b[
0
]);
}
}
结果如下:
--
Before clone
dc.a
=
10
dc.b
=
6
Shallow Copy
--
After clone
dc.a
=
10
dc.b
=
6
dcCopy.a
=
1
dcCopy.b
=
1
writeObject方法会将被克隆类的对象关系网都写出,这样就可以实现深克隆。当然,每个被克隆的成员类型必须实现Serializable接口