Java設計吉林快三平臺出租出售模式學習記錄-原型模式
阿新 • • 發佈:2018-07-09
單點 兩個 執行 cto trace param otsu execption string 今天要介紹的原型模式也是創建型模式中的一種,感覺叫復制方法模式或許更接地氣一些,我的理解就是用一個對象復制出另一對象。例如孫悟空拔幾根猴毛就能變出好幾個一樣的孫猴子來。其中孫悟空就是一個原型,創建孫猴子的過程就是實現原型模式的過程。 原型模式吉林快三平臺出租出售企鵝3266397597【dashengba.com大聖源碼論壇下載】原型模式介紹 原型模式是指使用原型實例來指定創建對象的種類,並且通過拷貝這些原型創建新的對象。 在使用原型模式時,我們需要首先創建一個原型對象,再通過復制這個原型對象,來創建更多的同類型的對象。 如何實現復制 原型模式中到底是如何實現復制的呢?下面介紹兩種實現方式。 1、通用的方式 通用的方式是在具體的原型類的復制方法中,實例化一個與自身類型一樣的對象,傳入相同的屬性值,然後將其返回。 如下代碼方式: public class PrototypeTest { //屬性變量 private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } / 復制方法 @return */ protected PrototypeTest clone() { PrototypeTest prototypeTest = new PrototypeTest(); prototypeTest.setName(name); return prototypeTest; } /* 測試 @param args / public static void main(String[] args) { PrototypeTest prototypeTest = new PrototypeTest(); prototypeTest.setName("第三"); //復制原型 PrototypeTest cloneObject = prototypeTest.clone(); System.out.println(Objects.toString(prototypeTest)); System.out.println(Objects.toString(cloneObject)); } } 輸出的結果是: PrototypeTest(name=第三) PrototypeTest(name=第三) 這種方式通用性很高,並且與編程語言特性無關,任何一種面向對象的語言都可以使用這種形式來實現對原型的復制。 2、Java中的Object的clone()方法 因為在Java中所有的Java類都繼承自java.lang.Object。而Object的類中提供一個默認的clone()方法,可以將一個Java對象復制一份。因此在Java中可以直接使用Object提供的clone()方法來實現對象的復制,這樣實現原型模式就比較簡單了。 需要註意的是,能夠調用clone()實現拷貝的Java類,必須實現一個標識接口Cloneable,表示這個Java類支持被復制,為什麽說是標識接口呢,因為這個接口裏面沒有定義任何方法,只是用了標識可以執行某些操作。如果一個類沒有實現這個接口但是調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedExecption異常。 如下代碼方式: public class PrototypeMain implements Cloneable{ private String name; private int age; 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; } / 重寫Object的clone方法 @return */ @Override protected PrototypeMain clone() { PrototypeMain prototypeMain = null; try { prototypeMain = (PrototypeMain)super.clone(); }catch (CloneNotSupportedException e){ System.err.println("Not Support Cloneable"); } return prototypeMain; } @Override public String toString() { return "PrototypeMain{" + "name=‘" + name + ‘\‘‘ + ", age=" + age + ‘}‘; } //測試 public static void main(String[] args) { PrototypeMain prototypeMain = new PrototypeMain(); prototypeMain.setName("小花"); prototypeMain.setAge(19); PrototypeMain cloneObject = prototypeMain.clone(); System.out.println(Objects.toString(cloneObject)); } } 運行結果: PrototypeMain{name=‘小花‘, age=19} 此時Object類可以理解為抽象原型類,而實現了Cloneable接口的類相當於具體原型類。 通過復制方法所創建的對象是全新的對象,它們在內存中擁有全新的地址,通常對復制所產生的對象進行修改時,對原型對象不會造成任何影響,每一個拷貝對象都是相互獨立的。通過不同的方式修改,可以得到一系列相似但不完全相同的對象。 原型模式的結構如下圖: 在原型模式結構圖中包含如下3個角色。 Prototype(抽象原型類):這是聲明復制方法的接口,是所有具體原型類的公共父類,可以是抽象類,也可以是接口,甚至可以是實現類。在上面介紹的實現復制的第二種方法裏面的java.lang.Object類就是擔當的這個角色。 ConcretePrototype(具體原型類):實現抽象原型類中聲明的復制方法,在復制方法中返回一個與自己同類的復制對象。在上面介紹的實現復制的第二種方法裏面的PrototypeMain類就是擔當的這個角色。 Client(客戶類):讓一個原型對象復制自身,從而創建一個新的的對象。在客戶類中只需要直接實例化或通過工廠方法等方式創建一個原型對象,再通過調用該對象的復制方法,就可以得到多個相同的對象了。在上面介紹的實現復制的第二種方法裏面,我將main方法寫在了具體原型類中,如果將main方法提出到一個新的的使用類中,那麽這個使用類就是客戶類。 深Copy與淺Copy 淺Copy是指被復制的對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用都指向原來的對象。簡單點說就是,只復制了引用,而沒有復制真正的對象內容。 深Copy是指被復制的對象的所有變量都含有與原來對象相同的值,屬性中的對象都指向被復制過的新對象中屬性,而不再是原型對象中的屬性。簡單點說,就是深Copy把所有的對象的引用以及對象都復制了一遍,在堆中是存在兩個相互獨立的對象,以及屬性中的對象也是相互獨立的。 我們還是舉例來說明吧: 如下代碼,創建一個原型類。 public class ShallowCopy implements Cloneable { //對象屬性 private ArrayList<String> nameList = new ArrayList<>(); / 復制方法 @return */ @Override protected ShallowCopy clone() { ShallowCopy shallowCopy = null; try{ shallowCopy = (ShallowCopy)super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return shallowCopy; } /* 獲得屬性 @return / public ArrayList<String> getNameList() { return nameList; } / 填充屬性值 @param name */ public void setNameList(String name) { this.nameList.add(name); } } 在客戶類種使用,進行復制。 public class ClientTest { @Test public void test(){ //創建一個對象 ShallowCopy shallowCopy = new ShallowCopy(); shallowCopy.setNameList("小紅"); //復制一個新對象 ShallowCopy newObject = shallowCopy.clone(); //給新對象的屬性賦值 newObject.setNameList("大黃"); System.out.println(shallowCopy.getNameList()); } } 預想的結果應該是:小紅,實際輸出: [小紅, 大黃] 產生這種結果的原因是因為Object類的clone()方法導致的,clone()方法在復制對象時,只是復制本對象的引用,對其內部的數組、引用對象等都不復制,還是指向原生對象的內部元素地址,這種復制方式就是淺Copy。在實際項目中使用這種方式的還是比較少的。一般內部的數組和引用對象才不復制,其他的原始類型int、long、double等類型是會被復制的。另外String類型也是會被復制的,String類裏是沒有clone()的。 那麽如何實現深Copy呢? 將上面的復制方法的代碼改造一下: / 復制方法 @return */ @Override protected ShallowCopy clone() { ShallowCopy shallowCopy = null; try{ shallowCopy = (ShallowCopy)super.clone(); shallowCopy.nameList = (ArrayList<String>) this.nameList.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return shallowCopy; } 其他內容不變,得到的輸出結果是: [小紅] 通過上述改造,我們實現了深Copy,這樣復制出來的新對象和原型對象之間沒有任何瓜葛了。實現了互相操作互不影響的效果,其實深Copy還有一種實現方式,那就是通過自己來寫二進制流來操作對象,然後實現對象的深Copy。 使用二進制流實現深Copy 將上面的深Copy代碼進行改造,改造後的代碼如下: public class ShallowCopy implements Serializable{ //對象屬性 private ArrayList<String> nameList = new ArrayList<>(); /* 復制方法 @return / @Override protected ShallowCopy clone() { ShallowCopy shallowCopy = null; try{ //寫入當前對象的二進制流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); //讀出二進制流產生新的對象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); shallowCopy = (ShallowCopy)ois.readObject(); }catch (IOException|ClassNotFoundException e){ e.printStackTrace(); } return shallowCopy; } / 獲得屬性 @return */ public ArrayList<String> getNameList() { return nameList; } /* 填充屬性值 @param name / public void setNameList(String name) { this.nameList.add(name); } } 客戶使用類內容不變。運行結果如下: [小紅] 需要註意的是通過這種方式來進行深Copy時,原型類必須實現Serializable接口,這樣才能將執行序列化將對象轉為二進制數據。 深Copy還有另一點需要註意的是,如果原型類中的屬性是一個引用類型的對象,這個屬性是不能用final修飾的,如果被final修飾後會編譯出錯。final修飾的屬性是不允許被重新賦值的。所以要使用深Copy時,在成員屬性上不要使用final.
Java設計吉林快三平臺出租出售模式學習記錄-原型模式