設計模式之原型模式

前言

在現實世界中,咱們一般會感受到分身乏術。要是本身有分身那該多好啊,一個用來工做,一個用來看電視,一個用來玩遊戲(無心中透露了本身單身狗的身份-。-),其實就是克隆,這種技術存在着很大的弊端,因此如今是禁止使用的。可是這種複製技術在java的世界裏早已出現,就是原型模式java

什麼是原型模式

用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象數據庫

uml類圖

clipboard.png

原型模式是設計模式中最簡單的,沒有之一。由於它的核心就是一個clone方法,經過這個方法完成對象的克隆。java提供了cloneable接口來標示這個對象是有可能被克隆的,這個接口只具備標記做用,在jvm中只有具備這個標記的對象纔有可能被克隆。有可能克隆變成能夠被克隆,就須要咱們重寫clone方法設計模式

淺拷貝

public class Person implements Cloneable{

    public Person() {
        System.out.println("構造函數執行了");
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setName("bin");

        Person person1=person.clone();
        person1.setName("123");
        System.out.println(person.getName()+"---"+person1.getName());
    }
}
測試結果:
構造函數執行了
bin---123

經過以上例子能夠得知,實現cloneable接口,複寫clone方法,便可對對象進行復制。有一個問題clone方法是怎麼建立對象的?能夠看到,構造函數只執行了一次,這就說明clone方法是不會調用構造函數的,它實際上是內存二進制流的拷貝,比直接new對象性能要好不少。
標題是淺拷貝,那到底什麼是淺拷貝呢?先不要着急,咱們來改動下例子:數組

public class Person implements Cloneable{

    private List<String> valueList = new ArrayList<>();

    public void setValue(){
        valueList.add("1");
    }
    public List<String> getValue(){
        return valueList;
    }
    
    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setValue();

        Person person1=person.clone();
        person1.setValue();
        System.out.println(person1.getValue());
    }
}
測試過程:
構造函數執行了
[1, 1]

或許你會很費解明明進行了拷貝,應該只會打印一個「1」啊,,爲何person1.getValue()會打印出兩個「1」呢?由於java作了一個偷懶的拷貝動做,Object提供的clone方法只拷貝本對象,對其引用對象和內部數組都不拷貝,只是將地址拷貝過來用,這種拷貝方式就是淺拷貝。可是String對象例外,由於java本就但願將String當作基本數據類型,它沒有clone方法,而且它的處理機制很是特殊,經過字符串池在內存中建立新的字符串,咱們只要把其看待成基本數據類型就能夠了。安全

深拷貝

當拷貝的對象中有引用對象或者數組時,咱們經過淺拷貝得到複製對象是不安全的。怎麼解決呢?咱們能夠經過深拷貝來解決這個問題,下面繼續改造例子學習深拷貝:jvm

public class Person implements Cloneable{

    private ArrayList<String> valueList = new ArrayList<>();

    public void setValue(){
        valueList.add("1");
    }
    public List<String> getValue(){
        return valueList;
    }

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person=(Person) super.clone();
        person.valueList= (ArrayList<String>) this.valueList.clone();
        return person;
    }
}
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setValue();

        Person person1=person.clone();
        person1.setValue();
        System.out.println(person.getValue());
        System.out.println(person1.getValue());
    }
}
測試結果:
person:[1]
person1[1, 1]

經過對引用對象和數組的進行獨立的拷貝,就完成了深拷貝,每一個person對象都會有本身的valueList。ide

clone和final

對象的clone和對象內的final是互相沖突的,下面咱們來看個圖片:函數

clipboard.png
當咱們將valueList增長final關鍵字,對其clone編譯會報錯,解決辦法就是不要加final關鍵字(感受像是廢話...)性能

總結

原型模式和現實世界中說的克隆基本同樣。原型模式是內存二進制流的拷貝,比new對象性能高不少,尤爲是當建立這個對象須要數據庫或者其餘硬件資源時尤其明顯。另外咱們須要注意的就是當複製的對象存在引用對象和數組時,要根據實際業務選擇深拷貝仍是淺拷貝。學習

相關文章
相關標籤/搜索