Java進階之深入理解深拷貝和淺拷貝

1 Java的clone方法

1.1 爲什麼需要拷貝?

在實際編程過程中,我們要遇到這種情況:有一個對象A,在某一時刻A中已經包含了一些有效值,此時可能會需要一個和A完全相同新對象B,並且此後對B任何改動都不會影響到A中的值。也就是說,A與B是兩個獨立的對象,但B的初始值是由A對象確定的。用賦值語句是不能滿足這種需求的,而通過實現clone()方法是最簡單、最高效的手段。

1.2 什麼是拷貝?

Java的所有類都默認繼承java.lang.Object類,在java.lang.Object類中有一個方法clone()。JDK API的說明文檔解釋這個方法將返回Object對象的一個拷貝。要說明的有兩點:

(1)拷貝對象返回的是一個新對象,而不是一個引用;
(2)拷貝對象與new新對象的區別就是:①這個拷貝已經包含了一些原來對象的信息,而不是對象的初始信息。 ②拷貝效率更高,因爲Object類的clone()一個native方法,native方法的效率都是遠高於非native方法。

1.2 如何使用clone()?

/**
 * 一個很典型的調用clone()代碼如下
 */
public class CloneClass implements Cloneable { 
 public int aInt; 
 
 public Object clone() { 
  CloneClass o = null; 
  try{ 
   o = (CloneClass)super.clone(); 
  } catch(CloneNotSupportedException e) { 
   e.printStackTrace(); 
  } 
  return o; 
 } 
}

有三個值得注意的地方:
(1)希望能實現clone功能的CloneClass類實現了Cloneable接口,這個接口屬於java.lang包,java.lang包已經被缺省的導入類中;
(2)重載clone()方法;
(3)在clone()方法中調用super.clone(),指無論clone類的繼承結構是什麼樣的,super.clone()直接或間接調用了java.lang.Object類的clone()方法;
(4)爲了讓其它類能調用這個clone類的clone()方法,重載之後要把clone()方法的屬性設置爲public;

2 淺拷貝

2.1 淺拷貝是什麼?

淺拷貝:對象A複製出一個對象B來,修改對象B中的8中基本類型(包括裝箱)的值和String,對象A中不被修改。修改對象B中的引用對象,由於只拷貝其引用,對象A中的引用對象也會被修改。

2.2 例子

public class AdEntity implements Cloneable {

    public String adTitle;

    public AdEntity(String adTitle) {
        this.adTitle = adTitle;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class BookshelfBanner implements Cloneable {
    /**
     * 標題
     */
    public String title;
    /**
     * banner圖片url
     */
    public String banner;
    /**
     * 廣告實體
     */
    public AdEntity adEntity;

    public BookshelfBanner(String title, AdEntity adEntity) {
        this.title = title;
        this.adEntity = adEntity;
    }

    /**
     * 淺拷貝
     * @return
     */
    public Object clone() {
        BookshelfBanner o = null;
        try {
            o = (BookshelfBanner) super.clone();
        } catch (CloneNotSupportedException e) {
            e.fillInStackTrace();
        }

        return o;
    }
}
public class Test {
    public static void main(String[] args) {
        AdEntity adEntity = new AdEntity("小書亭");
        BookshelfBanner banner1 = new BookshelfBanner("小書亭下載", adEntity);
        BookshelfBanner banner2 = (BookshelfBanner) banner1.clone();

        // banner2.adEntity.adTitle == "小書亭"
        adEntity.adTitle = "小書亭改名啦";              // adEntity引用的adTitle改變了
        // banner2.adEntity.adTitle == "小書亭改名啦"   // 淺拷貝adEntity,會被修改
    }
}

3 深拷貝

3.1 深拷貝是什麼?

深拷貝:對象A複製出一個對象B來,修改對象B中的8中基本類型(包括裝箱)的值和String,對象A中不被修改。修改對象B中的引用對象,由於引用對象是新創建對象,對象A中的引用對象不會被修改。

3.2 例子

public class AdEntity implements Cloneable {

    public String adTitle;

    public AdEntity(String adTitle) {
        this.adTitle = adTitle;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class BookshelfBanner implements Cloneable {

    /**
     * 標題
     */
    public String title;
    /**
     * banner圖片url
     */
    public String banner;
    /**
     * 廣告實體
     */
    public AdEntity adEntity;

    public BookshelfBanner(String title, AdEntity adEntity) {
        this.title = title;
        this.adEntity = adEntity;
    }

    /**
     * 深拷貝
     *
     * @return
     */
    public Object clone() {
        BookshelfBanner o = null;
        try {
            o = (BookshelfBanner) super.clone();
            if (adEntity != null) {
                o.adEntity = (AdEntity) adEntity.clone();
            }
        } catch (CloneNotSupportedException e) {
            e.fillInStackTrace();
        }

        return o;
    }
}
public class Test {
    public static void main(String[] args) {
        AdEntity adEntity = new AdEntity("小書亭");
        BookshelfBanner banner1 = new BookshelfBanner("小書亭下載", adEntity);
        BookshelfBanner banner2 = (BookshelfBanner) banner1.clone();

        // banner2.adEntity.adTitle == "小書亭"
        adEntity.adTitle = "小書亭改名啦";         // adEntity引用的adTitle改變了
        // banner2.adEntity.adTitle == "小書亭"   // 深拷貝adEntity,不會被修改
    }
}

4 還有一種深拷貝方法,將對象序列化

4.1 對象序列化是什麼?

對象序列化卻很耗時,在一些框架中,我們便可以感受到,它們往往將對象進行串行化後進行傳遞,耗時較多。

4.2 例子

public class AdEntity implements Serializable {

    public String adTitle;

    public AdEntity(String adTitle) {
        this.adTitle = adTitle;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class BookshelfBanner implements Serializable {

    /**
     * 標題
     */
    public String title;
    /**
     * banner圖片url
     */
    public String banner;
    /**
     * 廣告實體
     */
    public AdEntity adEntity;

    public BookshelfBanner(String title, AdEntity adEntity) {
        this.title = title;
        this.adEntity = adEntity;
    }

    /**
     * 深拷貝
     *
     * @return
     */
    public Object deepClone() throws IOException, OptionalDataException, ClassNotFoundException {
        // 將對象寫到流裏
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(this);
        // 從流裏讀出來
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        return (oi.readObject());
    }
}
public class Test {
    public static void main(String[] args) {
        AdEntity adEntity = new AdEntity("小書亭");
        BookshelfBanner banner1 = new BookshelfBanner("小書亭下載", adEntity);
        BookshelfBanner banner2 = (BookshelfBanner) banner1.deepClone();

        // banner2.adEntity.adTitle == "小書亭"
        adEntity.adTitle = "小書亭改名啦";  // adEntity引用的adTitle改變了
        // banner2.adEntity.adTitle == "小書亭"   // 深拷貝adEntity,不會被修改
    }
}

5 學習鏈接

Java中的深拷貝(深複製)和淺拷貝(淺複製)

Java的clone方法

淺拷貝和深拷貝(談談java中的clone)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章