成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

我的Java設(shè)計(jì)模式-原型模式

_ang / 1225人閱讀

摘要:序列化實(shí)現(xiàn)深拷貝這是實(shí)現(xiàn)深拷貝的另一種方式,通過二進(jìn)制流操作對象,從而達(dá)到深拷貝的效果。

“不好意思,我是臥底!哇哈哈哈~”額......自從寫了上一篇的觀察者模式,就一直沉浸在這個(gè)角色當(dāng)中,無法自撥。昨晚在看《使徒行者2》,有一集說到啊炮仗哥印鈔票,我去,這就是想印多少就印多少的節(jié)奏。

但是我覺得他們印鈔票的方法太low了,就用那“哧咔,哧咔~”的老機(jī)器沒日沒夜的印,看著都著急。

這里我們可以用原型模式優(yōu)化印鈔票的致富之路,為什么,繼續(xù)往下看......

一、原型模式 定義

??用原型實(shí)例指定所有創(chuàng)建對象的類型,并且通過復(fù)制這個(gè)拷貝創(chuàng)建新的對象。

特點(diǎn)

??1)必須存在一個(gè)現(xiàn)有的對象,也就是原型實(shí)例,通過原型實(shí)例創(chuàng)建新對象。

??2)在Java中,實(shí)現(xiàn)Cloneable,并且因?yàn)樗械念惗祭^承Object類重寫clone()方法來實(shí)現(xiàn)拷貝。

使用場景

大量的對象,并且類初始化時(shí)消耗的資源多。沒人會嫌錢多的吧,除了某云。

這些鈔票的信息屬性基本一致,可以調(diào)整個(gè)別的屬性。

印鈔票的工序非常復(fù)雜,需要進(jìn)行繁瑣的數(shù)據(jù)處理。

UML圖

從上面的UML圖可以看出,原型模式涉及到的角色有如下三個(gè):

??- 客戶端角色:負(fù)責(zé)創(chuàng)建對象的請求。

??- 抽象原型角色:該角色是一個(gè)抽象類或者是接口,提供拷貝的方法。

??- 具體原型角色:該角色是拷貝的對象,需要重寫抽象原型的拷貝方法,實(shí)現(xiàn)淺拷貝或者深拷貝。

二、實(shí)戰(zhàn)

一起來印鈔票,鈔票實(shí)例必須實(shí)現(xiàn)Cloneable接口,該接口只充當(dāng)一個(gè)標(biāo)記,然后重寫clone方法,具體原型角色代碼如下:

public class Money implements Cloneable {

    private int faceValue;

    private Area area;

    public int getFaceValue() {
        return faceValue;
    }

    public void setFaceValue(int faceValue) {
        this.faceValue = faceValue;
    }

    public Money(int faceValue, Area area) {
        this.faceValue = faceValue;
        this.area = area;
    }

    public Area getArea() {
        return area;
    }

    public void setArea(Area area) {
        this.area = area;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    @Override
    protected Money clone() throws CloneNotSupportedException {
        return (Money) super.clone();
    }
}

Area類代碼如下:

public class Area {

    // 鈔票單位
    private String unit;

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

}

看看客戶端如何實(shí)現(xiàn)鈔票的拷貝,代碼如下:

public class Client {

    public static void main(String[] args) {

        Area area = new Area();
        area.setUnit("RMB");

        // 原型實(shí)例,100RMB的鈔票
        Money money = new Money(100, area);

        for (int i = 1; i <= 3; i++) {
            try {
                Money cloneMoney = money.clone();
                cloneMoney.setFaceValue(i * 100);
                System.out.println("這張是" + cloneMoney.getFaceValue() +  cloneMoney.getArea().getUnit() + "的鈔票");
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
    }
}

大把大把的鈔票出來了

這張是100RMB的鈔票

這張是200RMB的鈔票

這張是300RMB的鈔票

從上面并沒有看到抽象原型角色的代碼,那該角色在哪?Object就是這個(gè)抽象原型角色,因?yàn)镴ava中所有的類都默認(rèn)繼承Objet,在這提供clone方法。

三、淺拷貝和深拷貝

在使用原型模式的時(shí)候,常常需要注意用的到底是淺拷貝還是深拷貝,當(dāng)然這必須結(jié)合實(shí)際的項(xiàng)目需求。下面來了解學(xué)習(xí)這兩種拷貝的用法和區(qū)別:

首先我們來看一個(gè)例子,只改變客戶端代碼:

public class Client {

    public static void main(String[] args) {

        Area area = new Area();
        area.setUnit("RMB");
        // 原型實(shí)例,100RMB的鈔票
        Money money = new Money(100, area);
        try {
            Money cloneMoney = money.clone();
            cloneMoney.setFaceValue(200);
            area.setUnit("美元"); 

            System.out.println("原型實(shí)例的面值:" + money.getFaceValue() +money.getArea().getUnit());
            System.out.println("拷貝實(shí)例的面值:" + cloneMoney.getFaceValue() + cloneMoney.getArea().getUnit());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }

}

運(yùn)行結(jié)果如下:

原型實(shí)例的面值:100美元

拷貝實(shí)例的面值:200美元

淺拷貝

見鬼了,明明就把原型實(shí)例的單位改成了美元而已,拷貝實(shí)例怎么也會跟著改變的。哪里有鬼?其實(shí)是Java在搞鬼。我們用的是Object的clone方法,而該方法只拷貝按值傳遞的數(shù)據(jù),比如String類型和基本類型,但對象內(nèi)的數(shù)組、引用對象都不拷貝,也就是說內(nèi)存中原型實(shí)例和拷貝實(shí)例指向同一個(gè)引用對象的地址,這就是淺拷貝。淺拷貝的內(nèi)存變化如下圖:

從上圖可以看出,淺拷貝前后的兩個(gè)實(shí)例對象共同指向同一個(gè)內(nèi)存地址,即它們共有擁有area1實(shí)例,同時(shí)也存在著數(shù)據(jù)被修改的風(fēng)險(xiǎn)。注意,這里不可拷貝的引用對象是指可變的類成員變量。

深拷貝

同樣的看例子,客戶端代碼如下:

public class Client {

    public static void main(String[] args) {

        Area area = new Area();
        area.setUnit("RMB");

        // 原型實(shí)例,100RMB的鈔票
        Money money = new Money(100, area);

        try {
            Money cloneMoney = money.clone();
            cloneMoney.setFaceValue(200);
            area.setUnit("美元");

            System.out.println("原型實(shí)例的面值:" + money.getFaceValue() + money.getArea().getUnit());
            System.out.println("拷貝實(shí)例的面值:" + cloneMoney.getFaceValue() + cloneMoney.getArea().getUnit());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }

}

運(yùn)行結(jié)果如下:

原型實(shí)例的面值:100美元

拷貝實(shí)例的面值:200RMB

咦~這客戶端代碼不是跟淺拷貝的一樣嗎,但是運(yùn)行結(jié)果卻又不一樣了。關(guān)鍵就在,實(shí)現(xiàn)深拷貝就需要完全的拷貝,包括引用對象,數(shù)組的拷貝。所以Area類也實(shí)現(xiàn)了Cloneable接口,重寫了clone方法,代碼如下:

public class Area implements Cloneable{

    // 鈔票單位
    private String unit;

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    @Override
    protected Area clone() throws CloneNotSupportedException {
        Area cloneArea;
        cloneArea = (Area) super.clone();
        return cloneArea;
    }
}

另外,在Money鈔票類的clone方法增加拷貝Area的代碼:

public class Money implements Cloneable, Serializable {

    private int faceValue;

    private Area area;

    public int getFaceValue() {
        return faceValue;
    }

    public void setFaceValue(int faceValue) {
        this.faceValue = faceValue;
    }

    public Money(int faceValue, Area area) {
        this.faceValue = faceValue;
        this.area = area;
    }

    public Area getArea() {
        return area;
    }

    public void setArea(Area area) {
        this.area = area;
    }

    @Override
    protected Money clone() throws CloneNotSupportedException {
        Money cloneMoney = (Money) super.clone();
        cloneMoney.area = this.area.clone();  // 增加Area的拷貝
        return cloneMoney;
    }

}

深拷貝的內(nèi)存變化如下圖:

深拷貝除了需要拷貝值傳遞的數(shù)據(jù),還需要拷貝引用對象、數(shù)組,即把所有引用的對象都拷貝。需要注意的是拷貝的引用對象是否還有可變的類成員對象,如果有就繼續(xù)對該成員對象進(jìn)行拷貝,如此類推。所以使用深拷貝是注意分析拷貝有多深,以免影響性能。

序列化實(shí)現(xiàn)深拷貝

這是實(shí)現(xiàn)深拷貝的另一種方式,通過二進(jìn)制流操作對象,從而達(dá)到深拷貝的效果。把對象寫到流里的過程是序列化過程,而把對象從流中讀出來的過程則叫反序列化過程。深拷貝的過程就是把對象序列化(寫成二進(jìn)制流),然后再反序列化(從流里讀出來)。注意,在Java中,常??梢韵仁箤ο髮?shí)現(xiàn)Serializable接口,包括引用對象也要實(shí)現(xiàn)Serializable接口,不然會拋NotSerializableException。

只要修改Money,代碼如下:

public class Money implements Serializable {

    private int faceValue;

    private Area area;

    public int getFaceValue() {
        return faceValue;
    }

    public void setFaceValue(int faceValue) {
        this.faceValue = faceValue;
    }

    public Money(int faceValue, Area area) {
        this.faceValue = faceValue;
        this.area = area;
    }

    public Area getArea() {
        return area;
    }

    public void setArea(Area area) {
        this.area = area;
    }

    @Override
    protected Money clone() throws CloneNotSupportedException {
        Money money = null;
        try {
            // 調(diào)用deepClone,而不是Object的clone方法
            cloneMoney = (Money) deepClone();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return cloneMoney;
    }

    // 通過序列化深拷貝
    public Object deepClone() throws IOException, ClassNotFoundException {
        //將對象寫到流里
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //從流里讀回來
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
}

同樣運(yùn)行客戶端代碼,最后來看看結(jié)果:

原型實(shí)例的面值:100美元

拷貝實(shí)例的面值:200RMB

四、原型模式的優(yōu)缺點(diǎn) 優(yōu)點(diǎn)

1)提高性能。不用new對象,消耗的資源少。

缺點(diǎn)

1)淺拷貝時(shí)需要實(shí)現(xiàn)Cloneable接口,深拷貝則要特別留意是否有引用對象的拷貝。

總結(jié)

原型模式本身比較簡單,重寫Object的clone方法,實(shí)現(xiàn)淺拷貝還是深拷貝。重點(diǎn)在理解淺拷貝和深拷貝,這是比較細(xì)但又重要,卻往往被忽略的知識點(diǎn)。好啦,原型模式就到這了,下一篇是策略模式,敬請關(guān)注,拜拜!

設(shè)計(jì)模式Java源碼GitHub下載https://github.com/jetLee92/DesignPattern

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/67872.html

相關(guān)文章

  • #yyds干貨盤點(diǎn)#Java設(shè)計(jì)模式之(四)——原型模式

    摘要:如果一個(gè)對象的初始化需要很多其他對象的數(shù)據(jù)準(zhǔn)備或其他資源的繁瑣計(jì)算,那么可以使用原型模式。當(dāng)需要一個(gè)對象的大量公共信息,少量字段進(jìn)行個(gè)性化設(shè)置的時(shí)候,也可以使用原型模式拷貝出現(xiàn)有對象的副本進(jìn)行加工處理。 1、什么是原型模式Specify the kinds of objects to create using a prot...

    番茄西紅柿 評論0 收藏2637
  • JavaScript 設(shè)計(jì)模式 ① 正確使用面向?qū)ο缶幊痰淖藙?/b>

    javascript是一門弱語言,他有著分同一般的靈活性使它迅速的成為幾乎人人必會的一門語言,but,你們使用的姿勢真的正確嗎? 在以前的開發(fā)過程當(dāng)中,老板:給我加個(gè)驗(yàn)證用戶郵箱、驗(yàn)證用戶短信...功能! function checkMessage(){...} function checkEmail(){...} function ... //茫茫多的函數(shù) 這樣寫好了之后 function 是全...

    macg0406 評論0 收藏0
  • JavaScript 設(shè)計(jì)模式 ① 正確使用面向?qū)ο缶幊痰淖藙?/b>

    javascript是一門弱語言,他有著分同一般的靈活性使它迅速的成為幾乎人人必會的一門語言,but,你們使用的姿勢真的正確嗎? 在以前的開發(fā)過程當(dāng)中,老板:給我加個(gè)驗(yàn)證用戶郵箱、驗(yàn)證用戶短信...功能! function checkMessage(){...} function checkEmail(){...} function ... //茫茫多的函數(shù) 這樣寫好了之后 function 是全...

    sf_wangchong 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<