您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Java中原型模式的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
還記得大四那年找工作,無意中我得從網(wǎng)上找到一份相對(duì)漂亮的程序員簡歷模板,然后全班同學(xué)開啟瘋狂的簡歷拷貝(U盤)。同時(shí)也鬧出了一個(gè)笑話,有幾位同學(xué),拷貝過去的簡歷,內(nèi)容完全沒改,名字都沒有改,截止投給面試官(校招面試官)。后來,結(jié)果大家也應(yīng)該能猜出來,大家都去實(shí)習(xí)了,部分人還在找工作。后面公司面試官和同伴的其他同學(xué)反饋:收到一毛一樣的簡歷,好幾份,回來大家一聊就知道問題出哪里了,承認(rèn)了自己拷貝過去完全沒改就拿出去投了,害,尷尬的一匹。
把簡歷拷貝分為為兩種:
一種是拷貝簡歷,然后把信息修改成自己的
另外一種是,拷貝簡歷,內(nèi)容什么都不改。
Specify the kinds of objects to create using a prototype instance ,and create new objects by coping this prototype
大致意思:用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過復(fù)制這些原型創(chuàng)建新的對(duì)象。
原型模式:Prototype Pattern,屬于創(chuàng)建型模式。
調(diào)用者不需要知道任何創(chuàng)建細(xì)節(jié),也不用調(diào)用構(gòu)造方法來創(chuàng)建對(duì)象。
原型模式有如下使用場景:
類初始化消耗資源較多
new產(chǎn)生的一個(gè)對(duì)象需要非常繁瑣的過程(數(shù)據(jù)準(zhǔn)備、訪問權(quán)限等)
構(gòu)造函數(shù)比較復(fù)雜
循環(huán)體內(nèi)生成大量對(duì)象時(shí)
在Spring中,原型模式應(yīng)用的非常廣泛,例如:scope='prototype'
我們可以將一些getter和setter之類封裝成一個(gè)工廠方法,然后對(duì)于使用的人來說,調(diào)用方法就可以了,不需要知道里面的getter和setter是怎么處理的。我們也可以使用JDK提供的實(shí)現(xiàn)Cloneable接口,實(shí)現(xiàn)快速復(fù)制。
創(chuàng)建對(duì)象的四種方式:
new、反射、克隆、序列化
大家是否有遇到過這種常見,就是項(xiàng)目中規(guī)定,不能把與數(shù)據(jù)庫表映射的entity類返回給前端,所以通常返回給前端的有各種O,比如:XxxVO、XxxBO、XxxDTO...
這時(shí)候就會(huì)出現(xiàn)下面的場景,大家也想已經(jīng)猜到了。
下面是與數(shù)據(jù)庫表映射的UserEntity實(shí)體類。
public class UserEntity { private Long id; private String name; private Integer age; //....可能還有很多屬性 //省略getter setter }
返回給前端或者調(diào)用方的UserVO實(shí)體類。
public class UserVO { private Long id; private String name; private Integer age; //....可能還有很多屬性 //省略getter setter }
此時(shí),從數(shù)據(jù)庫里查出來的UserEntity需要轉(zhuǎn)換成UserVO,然后再返回給前端(或者調(diào)用方)。
public class ObjectConvertUtil { public static UserVo convertUserEntityToUserVO(UserEntity userEntity) { if (userEntity == null) { return null; } UserVo userVo = new UserVo(); userVo.setId(userEntity.getId()); userVo.setName(userEntity.getName()); userVo.setAge(userEntity.getAge()); //如果還有更多屬性呢? return userVo; } }
從這個(gè)util類中,我們可以看出,如果一個(gè)類的屬性有幾十個(gè),上百個(gè)的,這代碼量是不是有點(diǎn)恐怖?
于是,我們通常都會(huì)使用一些工具類來處理,比如常見有以下:
BeanUtils.copy(); JSON.parseObject() Guava工具類 .....
這些工具類就用到了原型模式。
通過一個(gè)對(duì)象,創(chuàng)建一個(gè)新的對(duì)象。
也把原型模式稱之為對(duì)象的拷貝、克隆。
其實(shí)對(duì)象的克隆分淺克隆和深克隆,下面我們就來聊聊淺克隆和深克隆。
淺克?。簞?chuàng)建一個(gè)新對(duì)象,新對(duì)象的屬性和原來對(duì)象完全相同,對(duì)于非基本類型屬性,仍指向原來對(duì)象的屬性所指向的對(duì)象的內(nèi)存地址。
深克?。簞?chuàng)建一個(gè)新對(duì)象,屬性中引用的其他對(duì)象也會(huì)被克隆,不再指向原有對(duì)象地址。
我們先來聊聊淺克隆,都喜歡由淺入深。
比如,我現(xiàn)在相對(duì)用戶信息User進(jìn)行克隆,但是User中有用戶地址信息UserAddress屬性。
以下是代碼的實(shí)現(xiàn):
//用戶地址信息 public class UserAddress implements Serializable{ private String province; private String cityCode; public UserAddress(String province, String cityCode) { this.province = province; this.cityCode = cityCode; } } //用戶信息 public class User implements Cloneable { private int age; private String name; //用戶地址信息 private UserAddress userAddress; //getter setter 省略 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } //測試 public class UserTest { public static void main(String[] args) throws Exception { User user = new User(); user.setAge(20); user.setName("田維常"); UserAddress userAddress = new UserAddress("貴州", "梵凈山"); user.setUserAddress(userAddress); User clone = (User) user.clone(); System.out.println("克隆前后UserAddress比較:" + (user.getUserAddress() == clone.getUserAddress())); } }
輸出結(jié)果
克隆前后 UserAddress 比較:true
兩個(gè)對(duì)象屬性 UserAddress 指向的是同一個(gè)地址。
這就是所謂的淺克隆,只是克隆了對(duì)象,對(duì)于該對(duì)象的非基本類型屬性,仍指向原來對(duì)象的屬性所指向的對(duì)象的內(nèi)存地址。
關(guān)系如下:
關(guān)于深克隆,我們來用一個(gè)很經(jīng)典的案例,西游記里的孫悟空。一個(gè)孫悟空能變成n多個(gè)孫悟空,手里都會(huì)拿著一個(gè)金箍棒。
按照前面的淺克隆,結(jié)果就是:孫悟空倒是變成很多孫悟空,但是金箍棒用的是同一根。
深克隆的結(jié)果是:孫悟空變成了很多個(gè),金箍棒也變成很多個(gè)根。
下面我們用代碼來實(shí)現(xiàn):
//猴子,有身高體重和生日 public class Monkey { public int height; public int weight; public Date birthday; }
孫悟空也是猴子,兵器 孫悟空有個(gè)金箍棒:
import java.io.Serializable; //孫悟空的金箍棒 public class JinGuBang implements Serializable{ public float h=100; public float d=10; //金箍棒變大 public void big(){ this.h *=10; this.d *=10; } //金箍棒變小 public void small(){ this.h /=10; this.d /=10; } }
齊天大圣孫悟空:
import java.io.*; import java.util.Date; //孫悟空有七十二變,拔猴毛生成一個(gè)金箍棒 //使用JDK的克隆機(jī)制, //實(shí)現(xiàn)Cloneable并重寫clone方法 public class QiTianDaSheng extends Monkey implements Cloneable, Serializable { public JinGuBang jinGuBang; public QiTianDaSheng() { this.birthday = new Date(); this.jinGuBang = new JinGuBang(); } @Override protected Object clone() throws CloneNotSupportedException { return this.deepClone(); } //深克隆 public QiTianDaSheng deepClone() { try { //內(nèi)存中操作完成、對(duì)象讀寫,是通過字節(jié)碼直接操作 //與序列化操作類似 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream bis = new ObjectInputStream(bais); //完成一個(gè)新的對(duì)象,底層是使用new創(chuàng)建的一個(gè)對(duì)象 //詳情可以了解readObject方法 QiTianDaSheng qiTianDaSheng = (QiTianDaSheng) bis.readObject(); //每個(gè)猴子的生日不一樣,所以每次拷貝的時(shí)候,把生日改一下 qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } catch (Exception ex) { ex.printStackTrace(); return null; } } //淺克隆,就是簡單的賦值 public QiTianDaSheng shalllowClone(QiTianDaSheng target) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); qiTianDaSheng.height = target.height; qiTianDaSheng.weight = target.weight; qiTianDaSheng.jinGuBang = target.jinGuBang; qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } }
接著我們就來測試一下:
public class DeepCloneTest { public static void main(String[] args) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); try { QiTianDaSheng newObject = (QiTianDaSheng) qiTianDaSheng.clone(); System.out.print("深克隆后 "); System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang)); } catch (Exception ex) { ex.printStackTrace(); } QiTianDaSheng newObject=qiTianDaSheng.shalllowClone(qiTianDaSheng); System.out.print("淺克隆后 "); System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang)); } }
輸出結(jié)果為:
深克隆后 金箍棒是否一直:false 淺克隆后 金箍棒是否一直:true
結(jié)論
深克隆后每個(gè)孫悟空都有自己的金箍棒,而淺克隆后每個(gè)孫悟空用的金箍棒實(shí)質(zhì)上還是同一根。
切記:深和淺,指的是克隆對(duì)象里的屬性(引用類型)是否指向同一個(gè)內(nèi)存地址。
為了更深刻的理解深克隆和淺克隆,我們回答文中的簡歷拷貝的故事。
深拷貝:拷貝一份簡歷,然后對(duì)簡歷中的信息進(jìn)行修改成自己的
淺拷貝:拷貝一份簡歷,簡歷內(nèi)容完全不變
優(yōu)點(diǎn):
Java 原型模式基于內(nèi)存二進(jìn)制流復(fù)制,比直接 new 的性能會(huì)更好一些。
可以利用深克隆保存對(duì)象狀態(tài),存一份舊的(克隆出來),在對(duì)其修改,可以充當(dāng)一個(gè)撤銷功能。
缺點(diǎn):
需要配置 clone 方法,改造時(shí)需要對(duì)已有類進(jìn)行修改,違背 “開閉原則”。
如果對(duì)象間存在多重嵌套引用時(shí),每一層都需要實(shí)現(xiàn)克隆。
我們從原型模式的定義,使用場景,真實(shí)案例、淺克隆、深克隆、優(yōu)缺點(diǎn)等方面,對(duì)原型模式進(jìn)行了一個(gè)全面的講解。
關(guān)于“Java中原型模式的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。