如何實現對象克隆與深拷貝?


  • 實現 Cloneable 接口,重寫 clone() 方法。
  • 不實現 Cloneable 接口,會報 CloneNotSupportedException 異常。
package constxiong.interview;
 
/**
 * 測試克隆
 * @author ConstXiong
 * @date 2019-06-18 11:21:21
 */
public class TestClone {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person(1, "ConstXiong");//創建對象 Person p1
        Person p2 = (Person)p1.clone();//克隆對象 p1
        p2.setName("其不答");//修改 p2的name屬性,p1的name未變
        System.out.println(p1);
        System.out.println(p2);
    }
    
}
 
/**
 * 人
 * @author ConstXiong
 * @date 2019-06-18 11:54:35
 */
class Person implements Cloneable {
    
    private int pid;
    
    private String name;
    
    public Person(int pid, String name) {
        this.pid = pid;
        this.name = name;
        System.out.println("Person constructor call");
    }
 
    public int getPid() {
        return pid;
    }
 
    public void setPid(int pid) {
        this.pid = pid;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
 
    @Override
    public String toString() {
        return "Person [pid:"+pid+", name:"+name+"]";
    }
    
}

 

打印結果

Person constructor call
Person [pid:1, name:ConstXiong]
Person [pid:1, name:其不答]

 

  • Object 的 clone() 方法是淺拷貝,即如果類中屬性有自定義引用類型,只拷貝引用,不拷貝引用指向的對象。

 

可以使用下面的兩種方法,完成 Person 對象的深拷貝。

方法1、對象的屬性的Class 也實現 Cloneable 接口,在克隆對象時也手動克隆屬性。

    @Override
    public Object clone() throws CloneNotSupportedException {
        DPerson p = (DPerson)super.clone();
        p.setFood((DFood)p.getFood().clone());
        return p;
    }
 

 

 

完整代碼

package constxiong.interview;
 
/**
 * 測試克隆
 * @author ConstXiong
 * @date 2019-06-18 11:21:21
 */
public class TestManalDeepClone {
 
    public static void main(String[] args) throws Exception {
        DPerson p1 = new DPerson(1, "ConstXiong", new DFood("米飯"));//創建Person 對象 p1
        DPerson p2 = (DPerson)p1.clone();//克隆p1
        p2.setName("其不答");//修改p2的name屬性
        p2.getFood().setName("面條");//修改p2的自定義引用類型 food 屬性
        System.out.println(p1);//修改p2的自定義引用類型 food 屬性被改變,p1的自定義引用類型 food 屬性也隨之改變,說明p2的food屬性,只拷貝了引用,沒有拷貝food對象
        System.out.println(p2);
    }
    
}
 
class DPerson implements Cloneable {
    
    private int pid;
    
    private String name;
    
    private DFood food;
    
    public DPerson(int pid, String name, DFood food) {
        this.pid = pid;
        this.name = name;
        this.food = food;
        System.out.println("Person constructor call");
    }
 
    public int getPid() {
        return pid;
    }
 
    public void setPid(int pid) {
        this.pid = pid;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public Object clone() throws CloneNotSupportedException {
        DPerson p = (DPerson)super.clone();
        p.setFood((DFood)p.getFood().clone());
        return p;
    }
 
    @Override
    public String toString() {
        return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
    }
 
    public DFood getFood() {
        return food;
    }
 
    public void setFood(DFood food) {
        this.food = food;
    }
    
}
 
class DFood implements Cloneable{
    
    private String name;
    
    public DFood(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
}

 

 

打印結果

Person constructor call
Person [pid:1, name:ConstXiong, food:米飯]
Person [pid:1, name:其不答, food:面條]

 

 

方法2、結合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷貝

結合 java.io.Serializable 接口,完成深拷貝

package constxiong.interview;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
public class TestSeriazableClone {
 
    public static void main(String[] args) {
        SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米飯"));//創建 SPerson 對象 p1
        SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1
        p2.setName("其不答");//修改 p2 的 name 屬性
        p2.getFood().setName("面條");//修改 p2 的自定義引用類型 food 屬性
        System.out.println(p1);//修改 p2 的自定義引用類型 food 屬性被改變,p1的自定義引用類型 food 屬性未隨之改變,說明p2的food屬性,只拷貝了引用和 food 對象
        System.out.println(p2);
    }
    
}
 
class SPerson implements Cloneable, Serializable {
    
    private static final long serialVersionUID = -7710144514831611031L;
 
    private int pid;
    
    private String name;
    
    private SFood food;
    
    public SPerson(int pid, String name, SFood food) {
        this.pid = pid;
        this.name = name;
        this.food = food;
        System.out.println("Person constructor call");
    }
 
    public int getPid() {
        return pid;
    }
 
    public void setPid(int pid) {
        this.pid = pid;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    /**
     * 通過序列化完成克隆
     * @return
     */
    public Object cloneBySerializable() {
        Object obj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            obj = ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return obj;
    }
 
    @Override
    public String toString() {
        return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
    }
 
    public SFood getFood() {
        return food;
    }
 
    public void setFood(SFood food) {
        this.food = food;
    }
    
}
 
class SFood implements Serializable {
    
    private static final long serialVersionUID = -3443815804346831432L;
    
    private String name;
    
    public SFood(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    
}

 

 

打印結果

Person constructor call
Person [pid:1, name:ConstXiong, food:米飯]
Person [pid:1, name:其不答, food:面條]

 



  

來一道刷了進BAT的面試題?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM