在現實世界中,咱們一般會感受到分身乏術。要是本身有分身那該多好啊,一個用來工做,一個用來看電視,一個用來玩遊戲(無心中透露了本身單身狗的身份-。-),其實就是克隆,這種技術存在着很大的弊端,因此如今是禁止使用的。可是這種複製技術在java的世界裏早已出現,就是原型模式java
用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象數據庫
原型模式是設計模式中最簡單的,沒有之一。由於它的核心就是一個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是互相沖突的,下面咱們來看個圖片:函數
當咱們將valueList增長final關鍵字,對其clone編譯會報錯,解決辦法就是不要加final關鍵字(感受像是廢話...)性能
原型模式和現實世界中說的克隆基本同樣。原型模式是內存二進制流的拷貝,比new對象性能高不少,尤爲是當建立這個對象須要數據庫或者其餘硬件資源時尤其明顯。另外咱們須要注意的就是當複製的對象存在引用對象和數組時,要根據實際業務選擇深拷貝仍是淺拷貝。學習