再谈java clone 以及 浅/深拷贝

时间:2022-07-07 19:57:24

简单对象的拷贝,直接使用其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(
1);
list.add(
2);
list.add(
3);
pojo
= new Pojo();
pojo.setV1(
111);
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(
12);
Dog newDog
= (Dog) myDog.clone();

System.
out.println(" myDog " + myDog );
System.
out.println(" newDog " + newDog );

// myDog.ShowName();
// newDog.ShowName();

myDog.setName(
"aaaaa");
myDog.setAge(
33);
myDog.getList().clear();
myDog.getPojo().setV1(
222);
myDog.getPojo().setV2(
"bbb");

// myDog.ShowName();
// newDog.ShowName();


System.
out.println(" myDog " + myDog );
System.
out.println(" newDog " + newDog );
}
}

 

打印

 myDog Dog [age=12, name=热狗, list=[1, 2, 3]], pojo=Pojo [v1=111, v2=aa]]
newDog Dog [age
=12, name=热狗, list=[1, 2, 3]], pojo=Pojo [v1=111, v2=aa]]
myDog Dog [age
=33, name=aaaaa, list=[]], pojo=Pojo [v1=222, v2=bbb]]
newDog Dog [age
=12, name=热狗, list=[]], pojo=Pojo [v1=222, 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(
1);
list.add(
2);
list.add(
3);
pojo
= new Pojo();
pojo.setV1(
111);
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(
12);
Dog newDog
= (Dog) myDog.clone();

System.
out.println(" myDog " + myDog );
System.
out.println(" newDog " + newDog );

// myDog.ShowName();
// newDog.ShowName();

myDog.setName(
"aaaaa");
myDog.setAge(
33);
myDog.getList().clear();
myDog.getPojo().setV1(
222);
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