02 原型模式

02 原型模式

Java中怎麼拷貝一個對象呢?

可以通過調用這個對象類型的構造器構造一個新對象,然後將要拷貝對象的屬性設置到新對象裏面。
1.Java中也有另一種不通過構造器來拷貝對象的方式,這種方式稱爲克隆。
2.Java提供了java.lang.Cloneable和java.lang.Object中的clone()方法來支持克隆。

使用條件

是用於創建重複的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
這種模式是實現了一個原型接口,該接口用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的數據庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。

寫法

如果希望一個類擁有克隆的行爲,要做一下幾步:
1.該類實現java.lang.Cloneable接口。
2.覆蓋Object的clone方法,將訪問修飾符改爲public,修改返回類型。
3.clone方法內部調用父類的clone方法。

/*
* 1.實現cloneable接口
* */
class Graphy implements Cloneable{
    Person writer;
    /*2.重寫clone方法,並注意將訪問修飾符改爲public,修改返回類型。*/
     @Override
    public  Graphy clone() throws CloneNotSupportedException {
         /*3. clone方法內部調用父類的clone方法。父類執行對對象的字段的克隆操作,強轉修改 返回類型*/
        return (Graphy)super.clone();
    }
    public Person getWriter() {
        return writer;
    }
    public void setWriter(Person writer) {
        this.writer = writer;
    }
    @Override
    public String toString() {
        return "graphy{" +
                "writer=" + writer +
                '}';
    }
}

# 爲啥這麼寫

## 原理剖析

由於這段源碼很簡單而且註釋寫得太好了,先直接粘貼出來,免得寫得不確切,影響讀者閱讀。

```java
package java.lang;
/**
 * A class implements the <code>Cloneable</code> interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * <p>
 * Invoking Object's clone method on an instance that does not implement the
 * <code>Cloneable</code> interface results in the exception
 * <code>CloneNotSupportedException</code> being thrown.
 * <p>
 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.
 * <p>
 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.
 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

一個類實現Cloneable接口表示可以有對象的克隆的行爲的合法性,如果不實現會拋錯CloneNotSupportedException
但這個接口中卻沒有提供clone方法(Cloneable接口中沒有任何方法,看起來更像是一個標記接口。 )
按照註解提示 我們通常的做法是重寫Object的clone方法並把其改爲public方法。
那麼我們來看看Object的clone方法的源碼和註釋吧

/**
 * Creates and returns a copy of this object.  The precise meaning
 * of "copy" may depend on the class of the object. The general
 * intent is that, for any object {@code x}, the expression:
 * <blockquote>
 * <pre>
 * x.clone() != x</pre></blockquote>
 * will be true, and that the expression:
 * <blockquote>
 * <pre>
 * x.clone().getClass() == x.getClass()</pre></blockquote>
 * will be {@code true}, but these are not absolute requirements.
 * While it is typically the case that:
 * <blockquote>
 * <pre>
 * x.clone().equals(x)</pre></blockquote>
 * will be {@code true}, this is not an absolute requirement.
 * <p>
 * By convention, the returned object should be obtained by calling
 * {@code super.clone}.  If a class and all of its superclasses (except
 * {@code Object}) obey this convention, it will be the case that
 * {@code x.clone().getClass() == x.getClass()}.
 * <p>
 * By convention, the object returned by this method should be independent
 * of this object (which is being cloned).  To achieve this independence,
 * it may be necessary to modify one or more fields of the object returned
 * by {@code super.clone} before returning it.  Typically, this means
 * copying any mutable objects that comprise the internal "deep structure"
 * of the object being cloned and replacing the references to these
 * objects with references to the copies.  If a class contains only
 * primitive fields or references to immutable objects, then it is usually
 * the case that no fields in the object returned by {@code super.clone}
 * need to be modified.
 * <p>
 * The method {@code clone} for class {@code Object} performs a
 * specific cloning operation. First, if the class of this object does
 * not implement the interface {@code Cloneable}, then a
 * {@code CloneNotSupportedException} is thrown. Note that all arrays
 * are considered to implement the interface {@code Cloneable} and that
 * the return type of the {@code clone} method of an array type {@code T[]}
 * is {@code T[]} where T is any reference or primitive type.
 * Otherwise, this method creates a new instance of the class of this
 * object and initializes all its fields with exactly the contents of
 * the corresponding fields of this object, as if by assignment; the
 * contents of the fields are not themselves cloned. Thus, this method
 * performs a "shallow copy" of this object, not a "deep copy" operation.
 * <p>
 * The class {@code Object} does not itself implement the interface
 * {@code Cloneable}, so calling the {@code clone} method on an object
 * whose class is {@code Object} will result in throwing an
 * exception at run time.
 *
 * @return     a clone of this instance.
 * @throws  CloneNotSupportedException  if the object's class does not
 *               support the {@code Cloneable} interface. Subclasses
 *               that override the {@code clone} method can also
 *               throw this exception to indicate that an instance cannot
 *               be cloned.
 * @see java.lang.Cloneable
 */
protected native Object clone() throws CloneNotSupportedException;

創建並返回這個類的克隆,準確的描述是這個類的實例對象的克隆。
流程是 如果這個類的對象已經存在,直接複製一份這個對象,裏面的屬性原封不動的拷貝過來,這涉及到了地址的變化。
如果一個對象沒有實現Cloneable接口,調用它的clone方法會拋出CloneNotSupportedException異常。但數組例外,可以認爲數組實現了Cloneable,所以可以直接調用其clone方法獲取克隆對象。

深克隆與淺克隆(深拷貝與淺拷貝的區別)

按照Object克隆方法描述中的規範,克隆得到對象應該獨立於被克隆對象,從根本上說,它們的內存地址(不嚴格的說)應該不同。但是如果有這種情況,一個可克隆類中包含其他的引用類型屬性,那麼克隆後會是什麼情況呢?舉個例子說明一下:
package xzc._04prototype;

/*
 * 總結:
 * 1.一個類實現cloneable接口
 * 1.1點接口源碼進去 發現這是一個 標記型號接口 就是 裏面沒有任何需要我們實現的接口
 * 1.2這是一個標記型接口 用於聲明合法性 不寫拋錯
 *
 * 2.重寫clong方法
 * 2.1 點開源碼會看到 這是一個 protected native 修飾的方法(意味着更底層是c++寫的方法)
 * 2.2 提示我們使用clong方法 必須寫實現Cloneable接口 可以看到所有數組默認已經實現了Cloneable接口可以不用我們去寫
 * 2.3 重寫Clone方法並在裏面用 super.clone()實現,將訪問修飾符改成public (非必須)修改返回類型爲該類的類型。
 *
 * 3.深拷貝於淺拷貝
 *
 * */
/*
 * 1.實現cloneable接口
 * */
class Graphy implements Cloneable {
    Person writer;
    /*2.重寫clone方法,並注意將訪問修飾符改爲public,修改返回類型。*/
    @Override
    public Graphy clone() throws CloneNotSupportedException {
        /*3. clone方法內部調用父類的clone方法。父類執行對對象的字段的克隆操作,強轉修改返回類型*/
        return (Graphy) super.clone();
    }
    public Person getWriter() {
        return writer;
    }
    public void setWriter(Person writer) {
        this.writer = writer;
    }
    @Override
    public String toString() {
        return "graphy{" +
                "writer=" + writer +
                '}';
    }
}
public class prototypeMode {
    public static void main(String[] args) throws CloneNotSupportedException {
        Graphy graphy = new Graphy();
        Person person = new Person();
        Graphy graphy_clone = graphy.clone();
        //graphy和graphy_clone是否指向相同的內存地址
        System.out.println("graphy_clone == graphy");
        System.out.println(graphy_clone == graphy);
        //graphy的writer和graphy_clone的writer是否指向相同的內存地址
        System.out.println("graphy_clone.getWriter() == graphy.getWriter()");
        System.out.println(graphy_clone.getWriter() == graphy.getWriter());
    }
}
class Person {
    String name = null;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "person{" +
                "name='" + name + '\'' +
                '}';
    }
}

執行結果

在這裏插入圖片描述

可見clone方法默認的行爲就是淺克隆。那麼也很容易想到,如果克隆一個對象,並遞歸的克隆其所有的引用類型屬性,這樣的方式就是深克隆了。

理解了原理我們做如下修改

@Override
public Graphy clone() throws CloneNotSupportedException {
    /*3. clone方法內部調用父類的clone方法。父類執行對對象的字段的克隆操作,強轉修改返回類型*/
    Graphy graphy= (Graphy) super.clone();
    Person person= graphy.getWriter().clone();
    graphy.setWriter(person);
    return graphy;
}

設置了下一層的遞歸克隆

對其屬性的類實現克隆接口

class Person implements Cloneable{
    String name = null;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public Person clone() throws CloneNotSupportedException {
        /*3. clone方法內部調用父類的clone方法。父類執行對對象的字段的克隆操作,強轉修改返回類型*/
        return (Person)super.clone();
    }
    @Override
    public String toString() {
        return "person{" +
                "name='" + name + '\'' +
                '}';
    }
}

在這裏插入圖片描述

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