再谈java clone 以及 浅/深拷贝

简单对象的拷贝,直接使用其clone方法 即可, 不会有什么问题:

class Dog implements Cloneable 

public Dog clone() {

int age;
String name; // getter setter Dog myDog = null;
try {
  myDog = (Dog) super.clone();
} catch (CloneNotSupportedException e) {
  e.printStackTrace();
}
  return myDog;
} // any test ...

如此简单!

不过,如果对象有嵌套,我们还是使用这个做法(浅拷贝), 不注意就会出问题:

package design.creator.prototype;

import java.util.ArrayList;
import java.util.List; /**
* 浅拷贝:
*/
class Dog implements Cloneable {
int age;
String name;
List list;
Pojo pojo; public Dog(String tempName) {
name = tempName;
list = new ArrayList<>();
list.add();
list.add();
list.add();
pojo = new Pojo();
pojo.setV1();
pojo.setV2("aa");
} // public void ShowName() {
// System.out.println(name);
// } public Dog clone() {
Dog myDog = null;
try {
myDog = (Dog) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return myDog;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public List getList() {
return list;
} public void setList(List list) {
this.list = list;
} @Override
public String toString() {
return "Dog [age=" + age + ", name=" + name +
", list=" + list + "]" + ", pojo=" + pojo + "]";
} public Pojo getPojo() {
return pojo;
} public void setPojo(Pojo pojo) {
pojo = pojo;
} } class Pojo {
int v1;
String v2;
public int getV1() {
return v1;
}
public void setV1(int v1) {
this.v1 = v1;
}
public String getV2() {
return v2;
}
public void setV2(String v2) {
this.v2 = v2;
}
@Override
public String toString() {
return "Pojo [v1=" + v1 + ", v2=" + v2 + "]";
} } public class PrototypeTest {
public static void main(String[] args) {
Dog myDog = new Dog("热狗");
myDog.setAge();
Dog newDog = (Dog) myDog.clone(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog ); // myDog.ShowName();
// newDog.ShowName(); myDog.setName("aaaaa");
myDog.setAge();
myDog.getList().clear();
myDog.getPojo().setV1();
myDog.getPojo().setV2("bbb"); // myDog.ShowName();
// newDog.ShowName(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog );
}
}

打印

 myDog Dog [age=, name=热狗, list=[, , ]], pojo=Pojo [v1=, v2=aa]]
newDog Dog [age=, name=热狗, list=[, , ]], pojo=Pojo [v1=, v2=aa]]
myDog Dog [age=, name=aaaaa, list=[]], pojo=Pojo [v1=, v2=bbb]]
newDog Dog [age=, name=热狗, list=[]], pojo=Pojo [v1=, v2=bbb]]

你可能不懂为什么newDog 的name没变化,而newDog 的pojo、list都发生了变化—— 原来java 的clone 方法把 String当做了普通字段并进行了深复制, 而其他对象类型数据仍然的浅复制。

那么正确的做法是:

package design.creator.prototype;

import java.util.ArrayList;
import java.util.List; /**
* 深度拷贝:
*/
class Dog implements Cloneable {
int age;
String name;
List list;
Pojo pojo; public Dog(String tempName) {
name = tempName;
list = new ArrayList<>();
list.add();
list.add();
list.add();
pojo = new Pojo();
pojo.setV1();
pojo.setV2("aa");
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public List getList() {
return list;
} public void setList(List list) {
this.list = list;
} @Override
public String toString() {
return "Dog [age=" + age + ", name=" + name +
", list=" + list + "]" + ", pojo=" + pojo + "]";
} public Pojo getPojo() {
return pojo;
} public void setPojo(Pojo pojo) {
pojo = pojo;
} public Dog clone() {
Dog myDog = null;
try {
myDog = (Dog) super.clone();
myDog.list = (List) ((ArrayList)myDog.list).clone(); // 想要调用其clone方法,必须1 implements Cloneable 2 对其重写clone
myDog.pojo = (Pojo) myDog.pojo.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return myDog;
}
} class Pojo implements Cloneable{
int v1;
String v2;
public int getV1() {
return v1;
}
public void setV1(int v1) {
this.v1 = v1;
}
public String getV2() {
return v2;
}
public void setV2(String v2) {
this.v2 = v2;
}
@Override
public String toString() {
return "Pojo [v1=" + v1 + ", v2=" + v2 + "]";
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
} } public class PrototypeTest {
public static void main(String[] args) {
Dog myDog = new Dog("热狗");
myDog.setAge();
Dog newDog = (Dog) myDog.clone(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog ); // myDog.ShowName();
// newDog.ShowName(); myDog.setName("aaaaa");
myDog.setAge();
myDog.getList().clear();
myDog.getPojo().setV1();
myDog.getPojo().setV2("bbb"); // myDog.ShowName();
// newDog.ShowName(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog );
}
}

总结:

clone 方法只是浅拷贝,也就是说他只对象的第一层属性进行拷贝,其对象中的对象是不会进行重新拷贝的, 而仅仅只是拷贝那个引用。 如果对象有嵌套,那么需要注意重写相关步骤,使用 深拷贝。

参考:

http://blog.csdn.net/jariwsz/article/details/8588570

http://blog.csdn.net/xiaofengcanyuexj/article/details/23212189

上一篇:JavaScript解决select下拉框中的内容太长显示不全的问题


下一篇:在VS2015 RC打开CTP中创建的工程