为什么要使用clone
为什么要使用clone,直接new一个对象不就好了吗,实际使用中,直接new一个对象也不好,解决不了"新数据"会影响原始数据的问题。 这里简单创建了一个测试对象WhyClone。
public class WhyClone {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后创建两个WhyClone 对象,并且使用一个对另一个赋值,赋值之后修改被赋值的内容。
static void whyClone(){
WhyClone wc1 = new WhyClone();
wc1.setName("wc1");
WhyClone wc2 = new WhyClone();
wc2 = wc1;
System.out.println(wc1);
System.out.println(wc2);
System.out.println(wc1.getName());
System.out.println(wc2.getName());
wc2.setName("wc2");
System.out.println(wc1);
System.out.println(wc2);
System.out.println(wc1.getName());
System.out.println(wc2.getName());
}
编译结果:
clone.WhyClone@7a07c5b4
clone.WhyClone@7a07c5b4
wc1
wc1
clone.WhyClone@7a07c5b4
clone.WhyClone@7a07c5b4
wc2
wc2
查看结果发现:wc2被wc1赋值后,wc1和wc2指向同一块地址clone.WhyClone@7a07c5b4,且内容相同,且给wc2赋值字符串”wc2“后,wc1的内容也变成”wc2“。通常开发者需要改变wc2的值且不影响wc1,这就是为什么要使用clone的原因。
clone分为浅拷贝和深拷贝。
浅拷贝
拷贝时,拷贝对象需要实现Cloneable接口,并重写clone() 。
public class ShallowClone implements Cloneable{
private String name;
private Inner inner;
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static class Inner{
public Inner(String tag) {
this.tag = tag;
}
public String tag;
}
}
测试代码:
static void shallowClone() throws CloneNotSupportedException {
ShallowClone sc1 = new ShallowClone();
sc1.setInner(new ShallowClone.Inner("inner"));
sc1.setName("sc1");
ShallowClone sc2 = (ShallowClone) sc1.clone();
System.out.println(sc1);
System.out.println(sc2);
System.out.println(sc1.getInner());
System.out.println(sc2.getInner());
System.out.println(sc1.getName());
System.out.println(sc2.getName());
System.out.println(sc1.getInner().tag);
System.out.println(sc2.getInner().tag);
System.out.println(sc1.getName() == sc2.getName());
sc2.setName("sc2");
sc2.getInner().tag = "outer";
System.out.println(sc1);
System.out.println(sc2);
System.out.println(sc1.getInner());
System.out.println(sc2.getInner());
System.out.println(sc1.getName());
System.out.println(sc2.getName());
System.out.println(sc1.getInner().tag);
System.out.println(sc2.getInner().tag);
}
编译结果: 比较编译结果发现:直接修改name属性,sc1和sc2并不互相影响,但是两者的Inner指向的是同一个,修改sc2对象Inner的tag属性后,sc1也被修改了,说明ShallowClone.Inner用的同一个,想要一刀两断又没有彻底了断,属于藕断丝连的情况,也就是clone不彻底,称之为浅拷贝。
深拷贝
深拷贝顾名思义就是除了是同一类型的外,所有属性都没有联系,新对象是一块完全独立的内存,两者之间没有任何影响。
public class DeepCloneOuter implements Cloneable{
public DeepCloneInner deepCloneInner;
@Override
public Object clone() throws CloneNotSupportedException {
DeepCloneOuter deepCloneOuter = (DeepCloneOuter) super.clone();
deepCloneOuter.deepCloneInner = (DeepCloneInner) deepCloneInner.clone();
return deepCloneOuter;
}
}
public class DeepCloneInner implements Cloneable, Serializable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试代码:
static void deepClone() {
System.out.println("*****************************");
DeepCloneOuter deepCloneOuter = new DeepCloneOuter();
deepCloneOuter.deepCloneInner = new DeepCloneInner();
deepCloneOuter.deepCloneInner.setName("source");
try {
DeepCloneOuter deepCloneOuter2 = (DeepCloneOuter) deepCloneOuter.clone();
System.out.println(deepCloneOuter);
System.out.println(deepCloneOuter2);
System.out.println(deepCloneOuter.deepCloneInner.getName());
System.out.println(deepCloneOuter2.deepCloneInner.getName());
System.out.println(deepCloneOuter.deepCloneInner.getName() == deepCloneOuter2.deepCloneInner.getName());
System.out.println(deepCloneOuter.deepCloneInner == deepCloneOuter2.deepCloneInner);
deepCloneOuter2.deepCloneInner.setName("clone");
System.out.println(deepCloneOuter);
System.out.println(deepCloneOuter2);
System.out.println(deepCloneOuter.deepCloneInner.getName());
System.out.println(deepCloneOuter2.deepCloneInner.getName());
System.out.println(deepCloneOuter.deepCloneInner.getName() == deepCloneOuter2.deepCloneInner.getName());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
编译结果:
clone.DeepCloneOuter@68de145
clone.DeepCloneOuter@27fa135a
source
source
true
false
clone.DeepCloneOuter@68de145
clone.DeepCloneOuter@27fa135a
source
clone
false
false
编译结果说明深拷贝拷中的对象除了长得像,初此之外没有任何联系。当拷贝对象比较复杂的时候,该拷贝方法需要单独拷贝很多东西,所以不常用,利用序列化拷贝对象较为常用。
序列化深拷贝
序列化深拷贝是通过序列化对象,之后反序列化,利用字节流写入被拷贝对象并读取后得到新对象,相当于用户参照被拷贝对象手动开辟了一块新内存供新对象使用。 需要拷贝的对象只要实现Serializable即可。
public class SerializationClone implements Serializable {
private String name;
private Inner inner;
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static class Inner implements Serializable{
private String tag;
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
}
}```
拷贝工具:
```java
public class CloneUtil {
public static <T extends Serializable> T clone(T objection){
T cloneObjection = null;
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(objection);
obs.close();
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
cloneObjection = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObjection;
}
}
测试代码:
static void serializationClone(){
SerializationClone serializationClone = new SerializationClone();
serializationClone.setName("serializationClone");
SerializationClone.Inner inner = new SerializationClone.Inner();
inner.setTag("serializationCloneTag");
serializationClone.setInner(inner);
System.out.println("**************原数据**********");
System.out.println(serializationClone);
System.out.println(serializationClone.getInner());
System.out.println(serializationClone.getName());
System.out.println(serializationClone.getInner().getTag());
SerializationClone serializationClone1 = CloneUtil.clone(serializationClone);
System.out.println("**************拷贝后**********");
System.out.println(serializationClone1);
System.out.println(serializationClone1.getInner());
System.out.println(serializationClone1.getName());
System.out.println(serializationClone1.getInner().getTag());
serializationClone1.setName("serializationClone1");
serializationClone1.getInner().setTag("serializationClone1Tag1");
System.out.println("**************修改后原数据**********");
System.out.println(serializationClone);
System.out.println(serializationClone.getInner());
System.out.println(serializationClone.getName());
System.out.println(serializationClone.getInner().getTag());
System.out.println("**************修改后拷贝数据**********");
System.out.println(serializationClone1);
System.out.println(serializationClone1.getInner());
System.out.println(serializationClone1.getName());
System.out.println(serializationClone1.getInner().getTag());
}
编译结果:
**************原数据**********
clone.SerializationClone@26a1ab54
clone.SerializationClone$Inner@3d646c37
serializationClone
serializationCloneTag
**************拷贝后**********
clone.SerializationClone@156643d4
clone.SerializationClone$Inner@123a439b
serializationClone
serializationCloneTag
**************修改后原数据**********
clone.SerializationClone@26a1ab54
clone.SerializationClone$Inner@3d646c37
serializationClone
serializationCloneTag
**************修改后拷贝数据**********
clone.SerializationClone@156643d4
clone.SerializationClone$Inner@123a439b
serializationClone1
serializationClone1Tag1
比较拷贝前后的对象发现两者地址都不一样,包括内部对象的地址,修改任何属性也不会相互影响。
深拷贝建议使用序列化深拷贝
|