什麼要使用克隆、如何實現克隆物件、序列化與反序列實現克隆的好處、深拷貝與淺拷貝

愛學習的華華發表於2020-11-14

1、為什麼要使用克隆

物件的拷貝需要使用克隆,如果想對一個物件進行處理,又想保留原有的資料進行接下來的操作,就需要使用克隆了,Java語言中克隆針對的是類的例項。

2、如何實現克隆物件

有兩種方式:
(1)實現Cloneable介面並重寫Object類中的clone()方法。
(2)實現Serializable介面,通過物件的序列化和反序列化實現克隆,可以實現真正的深度克隆。
程式碼舉例


public class MyUtil {
     private MyUtil() {
        throw new AssertionError();
    }
 
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) 
                                  throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);
 
        ByteArrayInputStream bin = 
                    new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();
         // 說明:呼叫ByteArrayInputStream
        //或ByteArrayOutputStream物件的close方法沒有任何意義
        // 這兩個基於記憶體的流只要垃圾回收器清理物件就能夠釋放資源,
        //這一點不同於對外部資源(如檔案流)的釋放
    }
}
class Person implements Serializable {
    private static final long serialVersionUID 
                              = -9102017020286042305L;
    private String name;    // 姓名
    private int age;        // 年齡
    private Car car;        // 座駕
    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ",
                       age=" + age + ", car=" + car + "]";
    }
 }
class Car implements Serializable {
    private static final long serialVersionUID 
                                = -5713945027627603702L;
 
    private String brand;       // 品牌
    private int maxSpeed;       // 最高時速
 
    public Car(String brand, int maxSpeed) {
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }
 
    public String getBrand() {
        return brand;
    }
 
    public void setBrand(String brand) {
        this.brand = brand;
    }
 
    public int getMaxSpeed() {
        return maxSpeed;
    }
 
    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
 
    @Override
    public String toString() {
        return "Car [brand=" + brand + ", 
                      maxSpeed=" + maxSpeed + "]";
    }
 }
class CloneTest {
 
    public static void main(String[] args) {
        try {
            Person p1 = new Person("郭靖", 33, new Car("Benz", 300));
            Person p2 = MyUtil.clone(p1);   // 深度克隆
            p2.getCar().setBrand("BYD");
            // 修改克隆的Person物件p2關聯的汽車物件的品牌屬性
            // 原來的Person物件p1關聯的汽車不會受到任何影響
            // 因為在克隆Person物件時其關聯的汽車物件也被克隆了
            System.out.println(p1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、序列化與反序列實現克隆的好處

基於序列化和反序列化實現的克隆不僅僅是深度克隆,更重要的是通過泛型限定,可以檢查出要克隆的物件是否支援序列化
這項檢查是編譯器完成的,不是在執行時丟擲異常,這種方案明顯優於使用Object類的clone方法克隆物件。讓問題再編譯時候暴露,總是好過把問題留到執行時。

4、深拷貝與淺拷貝

深拷貝將物件及值複製過來,兩個物件修改其中任意的值,另一個值不會改變。
深克隆就是原型物件不管成員變數是什麼型別都要複製一份克隆物件,原型物件和克隆物件是兩個完全獨立的物件。
例如JSON.parse()和JSON.stringfy(),但是此方法無法複製函式型別。
在這裡插入圖片描述

淺拷貝只是複製了物件的引用地址,兩個物件指向同一個記憶體地址,所以修改其中任意值,另一個值都會隨之改變。
在這裡插入圖片描述
參考文章
https://blog.csdn.net/Certain_/article/details/108116710
https://blog.csdn.net/weixin_41847891/article/details/100213928
https://blog.csdn.net/ilovekdd/article/details/108812255

相關文章