再谈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(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

原文地址:https://www.cnblogs.com/FlyAway2013/p/3872990.html