原型模式(ProtoType) - Java里的对象复制
一, 引用的復(fù)制和對(duì)象復(fù)制.
在編程中, 我們有時(shí)會(huì)用兩個(gè)引用指向同一個(gè)對(duì)象.
例如:
ArrayList a = new ArrayLIst(); ArrayList b = a;看起來(lái)好像有a,b兩個(gè)容器, 實(shí)際上a,b是兩個(gè)引用, 它們都指向同1個(gè)Object的內(nèi)存地址.
而對(duì)象復(fù)制是指:
在內(nèi)存里劃分一塊與指定對(duì)象相同內(nèi)容的內(nèi)存.
也就是說(shuō)內(nèi)存里原理有1個(gè)Object的內(nèi)存, 復(fù)制后就有兩個(gè)了...
二, 對(duì)象復(fù)制的簡(jiǎn)便方法.
當(dāng)然, 對(duì)象復(fù)制的實(shí)現(xiàn)十分簡(jiǎn)單, 只需要實(shí)例化1個(gè)具有相同屬性的對(duì)象就ok了.
注意, 如果用 "==" 來(lái)講比較兩個(gè)引用是返回false, 因?yàn)樗鼈兊牡刂凡煌?
舉個(gè)列子:
2.1 產(chǎn)品類Prod:
public class Prod {private int prodID;private String prodName;public int getProdID() {return prodID;}public void setProdID(int prodID) {this.prodID = prodID;}public String getProdName() {return prodName;}public void setProdName(String prodName) {this.prodName = prodName;}@Overridepublic String toString(){return "Prod: " + this.getProdID() + " , " + this.getProdName();} }2.2 訂單類Order:
public class Order {private int orderID;private Prod prod;private int amount;public int getOrderID() {return orderID;}public void setOrderID(int orderID) {this.orderID = orderID;}public Prod getProd() {return prod;}public void setProd(Prod prod) {this.prod = prod;}public int getAmount() {return amount;}public void setAmount(int amount) {this.amount = amount;}public String toString(){return "Order: " + this.getOrderID() + " , " + this.getAmount() + " " + this.getProd().toString();} }2.3 客戶端代碼:
這里我先實(shí)例化1個(gè)order類 od1, 然后在復(fù)制1個(gè)od1 的對(duì)象od2
Prod p1 = new Prod();p1.setProdID(1);p1.setProdName("Hammer");Order od1 = new Order();od1.setOrderID(1);od1.setProd(p1);od1.setAmount(20);Prod p2 = new Prod();p2.setProdID(1);p2.setProdName("Hammer");Order od2 = new Order();od2.setOrderID(1);od2.setProd(p1);od2.setAmount(20);System.out.println(od1);System.out.println(od2);輸出: Order: 1 , 20 Prod: 1 , Hammer Order: 1 , 20 Prod: 1 , Hammer
2.4 這種方法的缺點(diǎn)
首先這種寫法不夠優(yōu)雅, 很多重復(fù)代碼.
其次, 這種復(fù)制方法調(diào)用了類的構(gòu)造函數(shù).
就如上面的例子, 即使Order類的構(gòu)造函數(shù)是空的, 但是實(shí)際上構(gòu)造函數(shù)的執(zhí)行機(jī)制很復(fù)雜,.
構(gòu)造函數(shù)最重要的動(dòng)作就是給類里的每個(gè)成員劃分內(nèi)存空間. 畢竟java是類c語(yǔ)言, 相當(dāng)于執(zhí)行了很多次malloc()函數(shù).
如果某些業(yè)務(wù)類的構(gòu)造函數(shù)寫的很復(fù)雜, 就更耗費(fèi)資源了.
而且
三, 利用Object.clone()方法來(lái)實(shí)現(xiàn)對(duì)象復(fù)制.
java里所有對(duì)象都是直接或簡(jiǎn)直繼承自基類Object.
而clone()是基類的Native 方法, 所謂Native方法可以認(rèn)為是java 內(nèi)部特定方法, 它比非Native的執(zhí)行效率要好.
也就是說(shuō), 利用Object.clone() 方法會(huì)比實(shí)例化1個(gè)相同內(nèi)容對(duì)象效率要好.
3.1 Object.clone()方法和 Cloneable接口:
Object.clone() 方法有點(diǎn)特殊,? 首先它是public方法, 也就是說(shuō)它可以通過(guò)引用名和"."來(lái)調(diào)用.
但是它不能被子類繼承, 也就說(shuō), 出了Object類外所有java里的class里面都沒有clone()這個(gè)方法的.
如果要使用Object.clone()方法.
只能引用1個(gè)叫做 Cloneable里的Interface,? 然后重寫Cloneable接口里的clone()方法, 在里面調(diào)用object.clone().
這個(gè)接口就起了1個(gè)標(biāo)記的作用, 幫組實(shí)現(xiàn)多態(tài).
3.2 訂單類Order:
我們按在這個(gè)思路來(lái)修改上面的訂單類Order: public class Order implements Cloneable{private int orderID;private Prod prod;private int amount;public int getOrderID() {return orderID;}public void setOrderID(int orderID) {this.orderID = orderID;}public Prod getProd() {return prod;}public void setProd(Prod prod) {this.prod = prod;}public int getAmount() {return amount;}public void setAmount(int amount) {this.amount = amount;}public String toString(){return "Order: " + this.getOrderID() + " , " + this.getAmount() + " " + this.getProd().toString();}@Overridepublic Object clone(){Object o = null;try{o = super.clone();}catch(Exception e){e.printStackTrace();}return o;} }值得注意的是, Object.clone() 回拋異常.
而, Prod類沒有任何修改.
3.3 客戶端代碼:
Prod p1 = new Prod();p1.setProdID(2);p1.setProdName("knife");Order od1 = new Order();od1.setOrderID(2);od1.setAmount(30);od1.setProd(p1);Order od2 = (Order)od1.clone();System.out.println(od1);System.out.println(od2);可見, 只需要執(zhí)行一句 Order od2 = (Order)od1.clone();
就相當(dāng)于 復(fù)制了1個(gè)對(duì)象.
3.4 UML圖
我們來(lái)看看這個(gè)例子的UML:
實(shí)際上這個(gè)就是原型模式(prototype)的UML圖了,
原來(lái)我們不知不覺得使用了原型模式.
四, 原型模式的定義
原型模式(protoType), 用原型實(shí)例制定創(chuàng)建對(duì)象的種類, 并且通過(guò)copy這些原型創(chuàng)建新的對(duì)象.
總覺得設(shè)計(jì)模式的定義都太過(guò)于簡(jiǎn)單惡心了.
上面的
原型指的是? Cloneable這個(gè)接口.
原型實(shí)例指的是 Order這個(gè)類.
五, 淺復(fù)制和深復(fù)制
對(duì)象復(fù)制也分兩種.
?????????? 兩個(gè)對(duì)象中的值類型成員屬性相等,? 對(duì)象成員成員屬性實(shí)際上沒有復(fù)制,都是同一個(gè)對(duì)象.
深復(fù)制:
????????? 兩個(gè)對(duì)象中的值類型成員屬性相等,? 對(duì)象成員屬性指向不同的 具有相同成員的 對(duì)象.
如上面的例子, 稍稍修改客戶端代碼:
Prod p1 = new Prod();p1.setProdID(2);p1.setProdName("knife");Order od1 = new Order();od1.setOrderID(2);od1.setAmount(30);od1.setProd(p1);Order od2 = (Order)od1.clone();System.out.println(od1);System.out.println(od2);od1.setOrderID(3);od1.setAmount(40);od1.getProd().setProdName("blade");System.out.println(od1);System.out.println(od2);上面 構(gòu)建了對(duì)象od1,
然后復(fù)制出了另1個(gè)對(duì)象od2,
然后修改od1的 值成員 和 對(duì)象成員
然后輸出od1, 和 od2 成員的值:
Order: 2 , 30 Prod: 2 , knife Order: 2 , 30 Prod: 2 , knife Order: 3 , 40 Prod: 2 , blade Order: 2 , 30 Prod: 2 , blade可見, 當(dāng)od1 的id 和 amount的值被改成 3 和 40 后,? od2 的id和 Amount的值并不受影響, 因?yàn)樗鼈兊闹殿愋统蓡T屬性是相互獨(dú)立的.
但是當(dāng)od1 的 prod屬性的內(nèi)容被修改后,??? od2 的也被修改了(knife - > blade),? 因?yàn)?od1 和 od2 的 prod成員指向的都是同1個(gè)對(duì)象.
這就證明了:
Java里的 Object.clone()方法實(shí)現(xiàn)的是淺復(fù)制.
六, 原型模式的深復(fù)制實(shí)現(xiàn)
接下來(lái)再次修改上面的例子, 令其實(shí)現(xiàn)成深復(fù)制
而且, 根據(jù)項(xiàng)目需要, 復(fù)制并不會(huì)復(fù)制Order的id, 也就是訂單號(hào)碼是唯一的, 只復(fù)制Prod和數(shù)量.
6.1 訂單類Order:
public class Order implements Cloneable{private int orderID;private Prod prod;private int amount;public int getOrderID() {return orderID;}public void setOrderID(int orderID) {this.orderID = orderID;}public Prod getProd() {return prod;}public void setProd(Prod prod) {this.prod = prod;}public int getAmount() {return amount;}public void setAmount(int amount) {this.amount = amount;}public String toString(){return "Order: " + this.getOrderID() + " , " + this.getAmount() + " " + this.getProd().toString();}@Overridepublic Object clone(){Prod p = new Prod();p.setProdID(this.getProd().getProdID());p.setProdName(this.getProd().getProdName());Order o = new Order();o.setOrderID(this.getOrderID() + 1);o.setAmount(this.getAmount());o.setProd(p);return o;} }客戶端輸出:
Order: 2 , 30 Prod: 2 , knife Order: 3 , 30 Prod: 2 , knife Order: 3 , 40 Prod: 2 , blade Order: 3 , 30 Prod: 2 , knife可以, 無(wú)論od1修改了什么,? od2 都不受影響
這種方法放棄了Object.clone(), Order類在重寫clone()方法里還是使用了構(gòu)造方法去實(shí)例化1個(gè)新的對(duì)象.
這種方法配置更加靈活(選擇性地復(fù)制成員)
當(dāng)時(shí)放棄了Object.clone()的效能優(yōu)點(diǎn).
但是這仍然實(shí)現(xiàn)了原型模式(protoType)
總結(jié)
以上是生活随笔為你收集整理的原型模式(ProtoType) - Java里的对象复制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工厂方法模式(Factory Metho
- 下一篇: java美元兑换,(Java实现) 美元