新聞中心
前言

創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供峨邊彝族網(wǎng)站建設(shè)、峨邊彝族做網(wǎng)站、峨邊彝族網(wǎng)站設(shè)計(jì)、峨邊彝族網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、峨邊彝族企業(yè)網(wǎng)站模板建站服務(wù),十載峨邊彝族做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
- 23種設(shè)計(jì)模式速記
- 單例(singleton)模式
- 工廠方法(factory method)模式
- 抽象工廠(abstract factory)模式
- 建造者/構(gòu)建器(builder)模式
23種設(shè)計(jì)模式快速記憶的請看上面第一篇,本篇和大家一起來學(xué)習(xí)原型模式,在學(xué)習(xí)原型模式之前我們需要先認(rèn)識(shí)下淺拷貝和深拷貝這兩個(gè)概念。
淺拷貝和深拷貝
淺拷貝
淺拷貝是按位拷貝對象,它會(huì)創(chuàng)建一個(gè)新對象,這個(gè)對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址 ,因此如果其中一個(gè)對象改變了這個(gè)地址,就會(huì)影響到另一個(gè)對象。
復(fù)制了對象的引用地址,兩個(gè)對象指向同一個(gè)內(nèi)存地址,所以修改其中任意的值,另一個(gè)值都會(huì)隨之變化。
深拷貝
深拷貝會(huì)拷貝所有的屬性,并拷貝屬性指向的動(dòng)態(tài)分配的內(nèi)存。當(dāng)對象和它所引用的對象一起拷貝時(shí)即發(fā)生深拷貝。深拷貝相比于淺拷貝速度較慢并且花銷較大。
將對象及值復(fù)制過來,兩個(gè)對象修改其中任意的值另一個(gè)值不會(huì)改變。
模式定義
用原型實(shí)例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。
原型模式其實(shí)就是一個(gè)對象在創(chuàng)建另一個(gè)可定制的對象,而且不需要指定任何創(chuàng)建的細(xì)節(jié)。Java提供了Coneable接口,其中有一個(gè)唯一方法Clone(),實(shí)現(xiàn)這個(gè)接口就可以完成原型模式了。
實(shí)例說明
淺拷貝案例
聲明 User 實(shí)體類,需要實(shí)現(xiàn) Cloneable 接口,并覆寫 clone() 方法。
User 屬性包括基礎(chǔ)數(shù)據(jù)類型和引用數(shù)據(jù)類型,方便演示
- package com.niuh.designpattern.prototype;
- /**
- * 用戶信息
- */
- public class User implements Cloneable {
- // 基礎(chǔ)數(shù)據(jù)類型
- private int id;
- private String name;
- private String sex;
- private String pwd;
- // 引用數(shù)據(jù)類型
- private BaseInfo baseInfo;
- public User(int id, String name, String sex, String pwd, BaseInfo baseInfo) {
- this.id = id;
- this.name = name;
- this.sex = sex;
- this.pwd = pwd;
- this.baseInfo = baseInfo;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getSex() {
- return sex;
- }
- public void setSex(String sex) {
- this.sex = sex;
- }
- public String getPwd() {
- return pwd;
- }
- public void setPwd(String pwd) {
- this.pwd = pwd;
- }
- public BaseInfo getBaseInfo() {
- return baseInfo;
- }
- public void setBaseInfo(BaseInfo baseInfo) {
- this.baseInfo = baseInfo;
- }
- @Override
- public String toString() {
- return "hashCode: " + super.hashCode() + ", User{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", sex='" + sex + '\'' +
- ", pwd='" + pwd + '\'' +
- ", baseInfo=" + baseInfo +
- '}';
- }
- @Override
- protected User clone() throws CloneNotSupportedException {
- return (User) super.clone();
- }
- }
- package com.niuh.designpattern.prototype;
- import java.util.Date;
- /**
- * 基礎(chǔ)類
- */
- public class BaseInfo {
- private String desc;
- // .......
- public BaseInfo(String desc) {
- this.desc = desc;
- }
- public String getDesc() {
- return desc;
- }
- public void setDesc(String desc) {
- this.desc = desc;
- }
- @Override
- public String toString() {
- return "BaseInfo{" +
- "desc=" + desc +
- '}';
- }
- }
- package com.niuh.designpattern.prototype;
- /**
- * 原型設(shè)計(jì)模式
- */
- public class PrototypePattern {
- public static void main(String[] args) throws CloneNotSupportedException {
- BaseInfo baseInfo = new BaseInfo("張三");
- User user1 = new User(1, "張三", "男", "123456", baseInfo);
- // new User ......
- // 克隆機(jī)制
- User user2 = user1.clone();
- user2.setId(2);
- user2.setName("李四");
- BaseInfo baseInfo1 = user2.getBaseInfo();
- baseInfo1.setDesc("李四");
- System.out.println(user1);
- System.out.println(user2);
- }
- }
輸出結(jié)果如下:
由輸出的結(jié)果可見,通過 user1.clone() 拷貝對象后得到的 user2,和 user1 是兩個(gè)不同的對象,HashCode 值不一樣。user1 和 user2 的基礎(chǔ)數(shù)據(jù)類型的修改互不影響,而引用類型 baseInfo 修改后是會(huì)有影響的。
深拷貝案例
通過上面的例子可以看到,淺拷貝會(huì)帶來數(shù)據(jù)安全方面的隱患,例如我們只是想修改了 user2 的 baseInfo,但是 user1 的 baseInfo 也被修改了,因?yàn)樗鼈兌际侵赶虻耐粋€(gè)地址。所以,此種情況下,我們需要用到深拷貝。
深拷貝,在拷貝引用類型成員變量時(shí),為引用類型的數(shù)據(jù)成員另辟了一個(gè)獨(dú)立的內(nèi)存空間,實(shí)現(xiàn)真正內(nèi)容上的拷貝。
對于 User 的引用類型的成員變量 BaseInfo ,需要實(shí)現(xiàn) Cloneable 并重寫 clone() 方法。
- package com.niuh.designpattern.prototype;
- import java.util.Date;
- /**
- * 基礎(chǔ)類
- */
- public class BaseInfo implements Cloneable {
- private String desc;
- // .......
- public BaseInfo(String desc) {
- this.desc = desc;
- }
- public String getDesc() {
- return desc;
- }
- public void setDesc(String desc) {
- this.desc = desc;
- }
- @Override
- public String toString() {
- return "BaseInfo{" +
- "desc=" + desc +
- '}';
- }
- @Override
- protected BaseInfo clone() throws CloneNotSupportedException {
- //BaseInfo 如果也有引用類型的成員屬性,也應(yīng)該和 User 類一樣實(shí)現(xiàn)
- return (BaseInfo) super.clone();
- }
- }
在 User 的 clone() 方法中,需要拿到拷貝自己后產(chǎn)生的新的對象,然后對新的對象的引用類型再調(diào)用拷貝操作,實(shí)現(xiàn)對引用類型成員變量的深拷貝。
- package com.niuh.designpattern.prototype;
- /**
- * 用戶信息
- */
- public class User implements Cloneable {
- // 基礎(chǔ)數(shù)據(jù)類型
- private int id;
- private String name;
- private String sex;
- private String pwd;
- // 引用數(shù)據(jù)類型
- private BaseInfo baseInfo;
- public User(int id, String name, String sex, String pwd, BaseInfo baseInfo) {
- this.id = id;
- this.name = name;
- this.sex = sex;
- this.pwd = pwd;
- this.baseInfo = baseInfo;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getSex() {
- return sex;
- }
- public void setSex(String sex) {
- this.sex = sex;
- }
- public String getPwd() {
- return pwd;
- }
- public void setPwd(String pwd) {
- this.pwd = pwd;
- }
- public BaseInfo getBaseInfo() {
- return baseInfo;
- }
- public void setBaseInfo(BaseInfo baseInfo) {
- this.baseInfo = baseInfo;
- }
- @Override
- public String toString() {
- return "hashCode: " + super.hashCode() + ", User{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", sex='" + sex + '\'' +
- ", pwd='" + pwd + '\'' +
- ", baseInfo=" + baseInfo +
- '}';
- }
- @Override
- protected User clone() throws CloneNotSupportedException {
- // 深拷貝
- User user = (User) super.clone();
- user.baseInfo = baseInfo.clone();
- return user;
- }
- }
與上面的使用方式一樣,輸出結(jié)果如下:
由輸出結(jié)果可見,深拷貝后,不管是基礎(chǔ)數(shù)據(jù)類型還是引用類型的成員變量,修改其值都不會(huì)相互造成影響。
序列化機(jī)制實(shí)現(xiàn)深拷貝
需要在 User 類實(shí)現(xiàn) Serializable,成員類型(BaseInfo)也需要實(shí)現(xiàn) Serializable 接口。
- package com.niuh.designpattern.prototype;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- /**
- * 用戶信息
- */
- public class User implements Cloneable , Serializable {
- // 基礎(chǔ)數(shù)據(jù)類型
- private int id;
- private String name;
- private String sex;
- private String pwd;
- // 引用數(shù)據(jù)類型
- private BaseInfo baseInfo;
- public User(int id, String name, String sex, String pwd, BaseInfo baseInfo) {
- this.id = id;
- this.name = name;
- this.sex = sex;
- this.pwd = pwd;
- this.baseInfo = baseInfo;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getSex() {
- return sex;
- }
- public void setSex(String sex) {
- this.sex = sex;
- }
- public String getPwd() {
- return pwd;
- }
- public void setPwd(String pwd) {
- this.pwd = pwd;
- }
- public BaseInfo getBaseInfo() {
- return baseInfo;
- }
- public void setBaseInfo(BaseInfo baseInfo) {
- this.baseInfo = baseInfo;
- }
- @Override
- public String toString() {
- return "hashCode: " + super.hashCode() + ", User{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", sex='" + sex + '\'' +
- ", pwd='" + pwd + '\'' +
- ", baseInfo=" + baseInfo +
- '}';
- }
- @Override
- protected User clone() throws CloneNotSupportedException {
- // 深拷貝
- // User user = (User) super.clone();
- // user.baseInfo = baseInfo.clone();
- // return user;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- try (ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) {
- oos.writeObject(this);
- } catch (IOException e) {
- e.printStackTrace();
- }
- ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
- try (ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)) {
- try {
- User user = (User) ois.readObject();
- return user;
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
這個(gè)時(shí)候并沒有使用Java深拷貝,改變成員屬性Baseinfo,也能保存對象的獨(dú)立性。
通過序列化機(jī)制來完成深拷貝不推薦使用,因?yàn)樾蛄谢僮魇荂PU密集型,解析流是比較消耗性能,速度會(huì)比較慢
優(yōu)點(diǎn)
- 可以不耦合具體類的情況下克隆對象
- 避免重復(fù)的初始化代碼
- 更方便的構(gòu)建復(fù)雜對象
缺點(diǎn)
- 適用性不是很廣。
- 每一個(gè)類必須配備一個(gè)克隆方法。
- 配備克隆方法需要對類的功能進(jìn)行通盤考慮,這對于全新的類不是很難,但對于已有的類不一定很容易,特別當(dāng)一個(gè)類引用不支持串行化的間接對象,或者引用含有循環(huán)結(jié)構(gòu)的時(shí)候。
應(yīng)用場景
當(dāng)代碼不應(yīng)該依賴于需要復(fù)制的對象的具體類時(shí),請使用Prototype模式。
- 某些結(jié)構(gòu)復(fù)雜的對象的創(chuàng)建工作;由于需求的變化,這些對象經(jīng)常面臨著劇烈的變化,但是他們卻擁有比較穩(wěn)定一致的接口;
- 一般在初始化的信息不發(fā)生變化的情況下,克隆是最好的方法。
源碼中的應(yīng)用
- #Spring
- org.springframework.beans.factory.support.AbstractBeanDefinition
- #JDK
- java.util.Arrays
- java.util.ArrayList
- ......
ArrayList中的使用
ArrayList也有clone()方法,如下
- 返回一個(gè)Object對象,所以在使用此方法的時(shí)候要強(qiáng)制轉(zhuǎn)換。
- ArrayList的本質(zhì)是維護(hù)了一個(gè)Object的數(shù)組,所以克隆也是通過數(shù)組的復(fù)制實(shí)現(xiàn)的,屬于淺復(fù)制。
- @Override
- public Object clone() {
- try {
- ArrayList> result = (ArrayList>) super.clone();
- result.array = array.clone();
- return result;
- } catch (CloneNotSupportedException e) {
- throw new AssertionError();
- }
- }
ArrayList的Clone淺復(fù)制的巧妙使用
當(dāng)你需要使用remove方法移除掉集合中的對象,而非要修改集合中的對象的時(shí)候,可以選擇使用。
- //添加兩個(gè)元素
- Student stJack=new Student("Jack", 13);
- Student stTom=new Student("Tom", 15);
- list.add(stJack);
- list.add(stTom);
- //克隆
- ArrayList
listCopy=(ArrayList ) list.clone(); - //移除且不修改
- listCopy.remove(1);
- System.out.println(list);
- System.out.println(listCopy);
移除且不修改集合中的元素,只是在List內(nèi)部的數(shù)組中移除了指向元素的地址,可以放心的使用clone。
PS:以上代碼提交在 Github :https://github.com/Niuh-Study/niuh-designpatterns.git
本文標(biāo)題:設(shè)計(jì)模式系列—原型模式
分享鏈接:http://m.5511xx.com/article/cdpephe.html


咨詢
建站咨詢
