国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

萬(wàn)字解析設(shè)計(jì)模式之單例模式

這篇具有很好參考價(jià)值的文章主要介紹了萬(wàn)字解析設(shè)計(jì)模式之單例模式。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、概述

1.1簡(jiǎn)介

單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。

這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類提供了一種訪問(wèn)其唯一的對(duì)象的方式,可以直接訪問(wèn),不需要實(shí)例化該類的對(duì)象。

1.2結(jié)構(gòu)

單例模式的主要有以下角色:

  • 單例類。只能創(chuàng)建一個(gè)實(shí)例的類
  • 訪問(wèn)類。使用單例類

1.3單例模式的實(shí)現(xiàn)

單例設(shè)計(jì)模式分類兩種:

餓漢式:類加載就會(huì)導(dǎo)致該單實(shí)例對(duì)象被創(chuàng)建

懶漢式:類加載不會(huì)導(dǎo)致該單實(shí)例對(duì)象被創(chuàng)建,而是首次使用該對(duì)象時(shí)才會(huì)創(chuàng)建

餓漢式-方式1(靜態(tài)變量方式)?

package com.yanyu.Singleton;

public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}
    //在成員位置創(chuàng)建該類的對(duì)象
    private static Singleton instance = new Singleton();
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        return instance;
    }
}

這是一個(gè)單例模式的實(shí)現(xiàn),確保在程序運(yùn)行中只有一個(gè)該類的實(shí)例對(duì)象存在。

具體實(shí)現(xiàn):

1. 將類的構(gòu)造方法私有化,防止外部直接通過(guò)構(gòu)造方法創(chuàng)建對(duì)象。

2. 在類的成員位置創(chuàng)建一個(gè)私有靜態(tài)的對(duì)象 instance,確保了Singleton類不能在外部被實(shí)例化,只能在類內(nèi)部創(chuàng)建對(duì)象。

3. 對(duì)外提供一個(gè)靜態(tài)的方法 getInstance(),返回該類的對(duì)象 instance,確保在程序中只有一個(gè)該類的實(shí)例對(duì)象存在

4. 由于 instance 是私有的靜態(tài)成員,在類加載時(shí)就已經(jīng)創(chuàng)建了該對(duì)象,所以在 getInstance() 方法中直接返回 instance 即可。由于instance是static成員,類的所有對(duì)象共享同一份instance,從而保證了在應(yīng)用中只有一個(gè)Singleton對(duì)象被創(chuàng)建。

需要注意的是,該實(shí)現(xiàn)方法并未考慮線程安全性,可能會(huì)存在線程安全問(wèn)題

使用單例模式可以避免重復(fù)創(chuàng)建對(duì)象,節(jié)省內(nèi)存空間,并且可以確保對(duì)象在程序中只有一個(gè)實(shí)例,保證數(shù)據(jù)一致性。

package com.yanyu.Singleton;

public class client{
    public static void main(String[]args){
        //創(chuàng)建singletion類的對(duì)象
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
            //判斷獲取到的兩個(gè)是否是同一個(gè)對(duì)象
        System.out.println(instance == instance1);
    }
}

萬(wàn)字解析設(shè)計(jì)模式之單例模式,設(shè)計(jì)模式,單例模式

?說(shuō)明:

該方式在成員位置聲明Singleton類型的靜態(tài)變量,并創(chuàng)建Singleton類的對(duì)象instance。instance對(duì)象是隨著類的加載而創(chuàng)建的。如果該對(duì)象足夠大的話,而一直沒(méi)有使用就會(huì)造成內(nèi)存的浪費(fèi)。

餓漢式-方式2(靜態(tài)代碼塊方式)

package com.yanyu.Singleton;

public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}
    //在成員位置創(chuàng)建該類的對(duì)象
    private static Singleton instance;
    static {
        instance = new Singleton();
    }
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        return instance;
    }
}

?這是一種餓漢式單例模式的實(shí)現(xiàn)方式,通過(guò)靜態(tài)代碼塊來(lái)初始化單例對(duì)象,保證了線程安全性和唯一性。在類被加載時(shí)就已經(jīng)創(chuàng)建了單例對(duì)象,因此也叫做餓漢式單例模式。

因?yàn)轭惣虞d是線程安全的,不需要考慮多線程的情況。

但是,由于對(duì)象是在類被加載時(shí)就創(chuàng)建的,因此可能會(huì)造成資源浪費(fèi)。如果該單例對(duì)象在程序運(yùn)行期間一直沒(méi)有被使用,那么一直占據(jù)著一部分內(nèi)存空間,會(huì)對(duì)系統(tǒng)的性能產(chǎn)生一定的影響。

另外,由于單例對(duì)象是靜態(tài)的,所以對(duì)于某些需要?jiǎng)討B(tài)實(shí)現(xiàn)的場(chǎng)景,該實(shí)現(xiàn)方式并不合適。

懶漢式-方式1(線程不安全)

package com.yanyu.Singleton;

public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}
    //在成員位置創(chuàng)建該類的對(duì)象
    private static Singleton instance;
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

?具體實(shí)現(xiàn)是通過(guò)私有構(gòu)造方法來(lái)禁止類的外部創(chuàng)建對(duì)象,然后在類的成員位置創(chuàng)建該類的唯一對(duì)象instance,并通過(guò)靜態(tài)方法getInstance()來(lái)返回該對(duì)象。在getInstance()方法中,先判斷instance是否為null,如果是則創(chuàng)建該對(duì)象,否則直接返回該對(duì)象。

這種實(shí)現(xiàn)方式稱為懶漢式單例模式,因?yàn)橹挥性诘谝淮握{(diào)用getInstance()方法時(shí)才會(huì)創(chuàng)建對(duì)象,而之后的調(diào)用都會(huì)直接返回已創(chuàng)建的對(duì)象。從而避免了多個(gè)實(shí)例導(dǎo)致的資源浪費(fèi)和數(shù)據(jù)不一致等問(wèn)題。

?懶漢式-方式2(線程安全)

package com.yanyu.Singleton;

public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}
    //在成員位置創(chuàng)建該類的對(duì)象
    private static Singleton instance;
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

?懶漢式相對(duì)于餓漢式來(lái)說(shuō),它是延遲加載的,只有在真正需要使用該對(duì)象時(shí)才會(huì)進(jìn)行初始化,這樣可以節(jié)省資源和減少初始化時(shí)間。但是,懶漢式實(shí)現(xiàn)需要考慮線程安全問(wèn)題,因?yàn)樵诙嗑€程環(huán)境下,如果多個(gè)線程同時(shí)調(diào)用 getInstance() 方法,可能會(huì)創(chuàng)建多個(gè)實(shí)例,導(dǎo)致單例模式失效。因此,需要使用 synchronized 關(guān)鍵字在方法內(nèi)部進(jìn)行同步,保證線程安全。

但是在getInstance()方法上添加了synchronized關(guān)鍵字,導(dǎo)致該方法的執(zhí)行效果特別低。

懶漢式-方式3(雙重檢查鎖)

對(duì)于?getInstance()?方法來(lái)說(shuō),絕大部分的操作都是讀操作,讀操作是線程安全的,所以我們沒(méi)必讓每個(gè)線程必須持有鎖才能調(diào)用該方法,我們需要調(diào)整加鎖的時(shí)機(jī)。由此也產(chǎn)生了一種新的實(shí)現(xiàn)模式:雙重檢查鎖模式?

package com.yanyu.Singleton;

public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}
    private static Singleton instance;
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        //第一次判斷,如果instance不為null,不進(jìn)入搶鎖階段,直接返回實(shí)際
        if(instance == null) {
            synchronized (Singleton.class) {
                //搶到鎖之后再次判斷是否為空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

是怕多線程創(chuàng)建多個(gè)對(duì)象,如果不為空直接返回提高效率,為空就要加鎖防止

首先進(jìn)行一次非空判斷,如果instance為null,才進(jìn)行同步代碼塊的搶鎖,再次判斷instance是否為空,確保只有一個(gè)線程可以創(chuàng)建對(duì)象。

雙重檢查鎖模式是一種非常好的單例實(shí)現(xiàn)模式,解決了單例、性能、線程安全問(wèn)題,上面的雙重檢測(cè)鎖模式看上去完美無(wú)缺,其實(shí)是存在問(wèn)題,在多線程的情況下,可能會(huì)出現(xiàn)空指針問(wèn)題,出現(xiàn)問(wèn)題的原因是JVM在實(shí)例化對(duì)象的時(shí)候會(huì)進(jìn)行優(yōu)化和指令重排序操作。

要解決雙重檢查鎖模式帶來(lái)空指針異常的問(wèn)題,只需要使用?volatile?關(guān)鍵字,?volatile?關(guān)鍵字可以保證可見(jiàn)性和有序性。

指令重排序問(wèn)題解釋

在多線程情況下,指令重排序可能會(huì)導(dǎo)致該模式的失效。當(dāng)一個(gè)線程搶到鎖之后,由于指令重排序的影響,實(shí)例變量可能會(huì)先被賦值到內(nèi)存中,但是還沒(méi)有調(diào)用構(gòu)造函數(shù),而此時(shí)另一個(gè)線程進(jìn)入該方法,會(huì)認(rèn)為instance不為空直接返回實(shí)例,但是此時(shí)實(shí)例并沒(méi)有完成初始化,會(huì)導(dǎo)致程序出錯(cuò)。

為了避免這種情況,需要在instance前添加volatile關(guān)鍵字,保證它能正確的被初始化。這樣可以確保其他線程在獲取instance的時(shí)候,總是從主內(nèi)存中獲取,而不是線程的本地內(nèi)存中獲取,從而避免了指令重排序帶來(lái)的問(wèn)題。

package com.yanyu.Singleton;

public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}
    private static volatile Singleton instance;
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        //第一次判斷,如果instance不為null,不進(jìn)入搶鎖階段,直接返回實(shí)際
        if(instance == null) {
            synchronized (Singleton.class) {
                //搶到鎖之后再次判斷是否為空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

?這是一種線程安全的懶漢式單例模式實(shí)現(xiàn)方式,主要特點(diǎn)有:

1. 構(gòu)造函數(shù)私有化,確保該類不能在外部通過(guò)構(gòu)造函數(shù)來(lái)創(chuàng)建對(duì)象。

2. 使用 volatile 關(guān)鍵字修飾 instance,保證在多線程環(huán)境下 instance 的可見(jiàn)性。

3. getInstance 方法加入雙重檢查鎖,確保在多線程環(huán)境下只有一個(gè)線程能夠創(chuàng)建 Singleton 實(shí)例,并且只有在 instance 為 null 的情況下才會(huì)創(chuàng)建實(shí)例。

4. 使用 synchronized 關(guān)鍵字對(duì) instance 進(jìn)行加鎖,保證在多線程環(huán)境下只有一個(gè)線程能夠進(jìn)入創(chuàng)建實(shí)例的代碼塊。

5. 返回 Singleton 實(shí)例的方法為靜態(tài)方法,能夠在類的外部方便地獲取該對(duì)象。

這種實(shí)現(xiàn)方式既保證了線程安全,又減少了同步的開(kāi)銷,是一種比較常用的單例模式實(shí)現(xiàn)方式。

懶漢式-方式4(靜態(tài)內(nèi)部類方式)

靜態(tài)內(nèi)部類單例模式中實(shí)例由內(nèi)部類創(chuàng)建,由于 JVM 在加載外部類的過(guò)程中, 是不會(huì)加載靜態(tài)內(nèi)部類的, 只有內(nèi)部類的屬性/方法被調(diào)用時(shí)才會(huì)被加載, 并初始化其靜態(tài)屬性。靜態(tài)屬性由于被?static?修飾,保證只被實(shí)例化一次,并且嚴(yán)格保證實(shí)例化順序。

package com.yanyu.Singleton;

public class Singleton {

    //私有構(gòu)造方法
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

這是一種利用靜態(tài)內(nèi)部類實(shí)現(xiàn)的線程安全的單例模式。在類加載器加載類的時(shí)候,靜態(tài)內(nèi)部類 SingletonHolder 不會(huì)被初始化,只有在 getInstance() 方法第一次被調(diào)用時(shí),才會(huì)被加載進(jìn)內(nèi)存并實(shí)例化,同時(shí)保證了線程安全。這種方式既保證了線程安全,也避免了同步帶來(lái)的性能損失,同時(shí)也實(shí)現(xiàn)了延遲加載。由于這種方式不需要加鎖,因此效率比較高。

?這種方式的核心思想是使用靜態(tài)內(nèi)部類來(lái)持有單例實(shí)例,因?yàn)?span style="color:#fe2c24;">靜態(tài)內(nèi)部類只會(huì)被加載一次,所以它的成員變量也只會(huì)被初始化一次,從而保證了線程安全。同時(shí),通過(guò)將單例對(duì)象的實(shí)例化延遲到內(nèi)部類加載時(shí)進(jìn)行,也實(shí)現(xiàn)了懶加載。

?枚舉方式

枚舉類實(shí)現(xiàn)單例模式是極力推薦的單例實(shí)現(xiàn)模式,因?yàn)槊杜e類型是線程安全的,并且只會(huì)裝載一次,設(shè)計(jì)者充分的利用了枚舉的這個(gè)特性來(lái)實(shí)現(xiàn)單例模式,枚舉的寫法非常簡(jiǎn)單,而且枚舉類型是所用單例實(shí)現(xiàn)中唯一一種不會(huì)被破壞的單例實(shí)現(xiàn)模式。

package com.yanyu.Singleton;

public enum Singleton {
    INSTANCE;
}

?INSTANCE是一個(gè)枚舉對(duì)象,也就是該單例類的唯一實(shí)例。通過(guò)這種方式獲取單例對(duì)象,可以保證在任何情況下都只有一個(gè)實(shí)例對(duì)象存在。即使在多線程的情況下也是安全的。可以避免多線程問(wèn)題和反射攻擊

package com.yanyu.Singleton;

public class client{
    public static void main(String[]args){
        //創(chuàng)建singletion類的對(duì)象
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;
            //判斷獲取到的兩個(gè)是否是同一個(gè)對(duì)象
        System.out.println(instance == instance1);
    }
}

在使用枚舉類型實(shí)現(xiàn)單例模式時(shí),反射攻擊是無(wú)效的,因?yàn)槊杜e類型的構(gòu)造方法是私有的,并且只會(huì)在類加載時(shí)被調(diào)用一次,保證了單例的唯一性。在使用反射獲取該單例時(shí),會(huì)拋出異常,因?yàn)槊杜e類型不支持反射創(chuàng)建實(shí)例。因此,枚舉類型實(shí)現(xiàn)單例模式是一種安全可靠的方式。

1.4破壞單例模式的方式?

使上面定義的單例類(Singleton)可以創(chuàng)建多個(gè)對(duì)象,枚舉方式除外。有兩種方式,分別是序列化和反射。

序列化反序列化

Singleton類:

package com.yanyu.Singleton;

import java.io.Serializable;

public class Singleton implements Serializable {

    //私有構(gòu)造方法
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Test類:

package com.yanyu.Singleton;


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    public static void main(String[] args) throws Exception {
        //往文件中寫對(duì)象
        //writeObject2File();
        //從文件中讀取對(duì)象
        Singleton s1 = readObjectFromFile();
        Singleton s2 = readObjectFromFile();
        //判斷兩個(gè)反序列化后的對(duì)象是否是同一個(gè)對(duì)象
        System.out.println(s1 == s2);
    }
    private static Singleton readObjectFromFile() throws Exception {
        //創(chuàng)建對(duì)象輸入流對(duì)象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt"));
        //第一個(gè)讀取Singleton對(duì)象
        Singleton instance = (Singleton) ois.readObject();
        return instance;
    }
    public static void writeObject2File() throws Exception {
        //獲取Singleton類的對(duì)象
        Singleton instance = Singleton.getInstance();
        //創(chuàng)建對(duì)象輸出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));
        //將instance對(duì)象寫出到文件中
        oos.writeObject(instance);
    }
}

上面代碼運(yùn)行結(jié)果是false,表明序列化和反序列化已經(jīng)破壞了單例設(shè)計(jì)模式。

當(dāng)單例模式中的實(shí)例被序列化成字節(jié)流并保存到文件系統(tǒng)或數(shù)據(jù)庫(kù)中時(shí),如果后續(xù)讀取對(duì)象并反序列化后,會(huì)生成一個(gè)新的實(shí)例,從而破壞了單例模式的原則。

這是因?yàn)椋谛蛄谢头葱蛄谢^(guò)程中,Java內(nèi)部使用了一個(gè)特殊的方法來(lái)創(chuàng)建對(duì)象,該方法不會(huì)調(diào)用任何構(gòu)造函數(shù),也不會(huì)檢查是否已經(jīng)存在該對(duì)象的實(shí)例。因此,如果單例模式本身沒(méi)有實(shí)現(xiàn)序列化和反序列化的特殊處理,就會(huì)導(dǎo)致破壞單例模式。

?序列化會(huì)將對(duì)象轉(zhuǎn)換為一個(gè)字節(jié)序列,以便在網(wǎng)絡(luò)上傳輸或保存到文件中。當(dāng)反序列化時(shí),會(huì)將字節(jié)序列轉(zhuǎn)換回對(duì)象。當(dāng)單例類被序列化為字節(jié)流時(shí),字節(jié)流中不包含單例類的狀態(tài),當(dāng)反序列化時(shí),新的實(shí)例會(huì)被創(chuàng)建出來(lái),這樣就違反了單例模式的原則,一個(gè)單例類就被破壞了。

反射?

Singleton類:

package com.yanyu.Singleton;


public class Singleton {
    //私有構(gòu)造方法
    private Singleton() {}

    private static volatile Singleton instance;
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        if(instance != null) {
            return instance;
        }
        synchronized (Singleton.class) {
            if(instance != null) {
                return instance;
            }
            instance = new Singleton();
            return instance;
        }
    }
}

Test類:

package com.yanyu.Singleton;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        //獲取Singleton類的字節(jié)碼對(duì)象
        Class clazz = Singleton.class;
        //獲取Singleton類的私有無(wú)參構(gòu)造方法對(duì)象
        Constructor constructor = clazz.getDeclaredConstructor();
        //取消訪問(wèn)檢查
        constructor.setAccessible(true);
        //創(chuàng)建Singleton類的對(duì)象s1
        Singleton s1 = (Singleton) constructor.newInstance();
        //創(chuàng)建Singleton類的對(duì)象s2
        Singleton s2 = (Singleton) constructor.newInstance();
        //判斷通過(guò)反射創(chuàng)建的兩個(gè)Singleton對(duì)象是否是同一個(gè)對(duì)象
        System.out.println(s1 == s2);
    }
}

上面代碼運(yùn)行結(jié)果是false,表明反射已經(jīng)破壞了單例設(shè)計(jì)模式

?單例模式可以被反射機(jī)制破壞,因?yàn)榉瓷錂C(jī)制可以通過(guò)修改類的私有構(gòu)造方法來(lái)創(chuàng)建一個(gè)新的實(shí)例,這樣就違背了單例模式的原則。此外,反射還可以通過(guò)修改單例類中的字段來(lái)改變單例實(shí)例的狀態(tài),從而破壞單例模式的行為。

1.5?問(wèn)題的解決

序列化、反序列方式解決

在Singleton類中添加readResolve()方法,在反序列化時(shí)被反射調(diào)用,如果定義了這個(gè)方法,就返回這個(gè)方法的值,如果沒(méi)有定義,則返回新new出來(lái)的對(duì)象。

Singleton類:

package com.yanyu.Singleton;

import java.io.Serializable;


public class Singleton implements Serializable {

    //私有構(gòu)造方法
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * //當(dāng),進(jìn)行反序列化時(shí),會(huì)白動(dòng)調(diào)用該力法,將該方法的返回值直接返回
     */
    private Object readResolve() {
        return SingletonHolder.INSTANCE;
    }
}

當(dāng)反序列化時(shí),會(huì)先調(diào)用 readObject() 方法,如果該類中存在 readResolve() 方法,會(huì)在 readObject() 方法執(zhí)行之后,將 readResolve() 方法的返回值直接返回,從而確保只有一個(gè)實(shí)例。在該示例中,readResolve() 方法返回 SingletonHolder 的 INSTANCE,即單例對(duì)象的唯一實(shí)例。

反射方式解決

為了防止這種破解,可以在單例類的構(gòu)造函數(shù)中添加判斷,如果已存在實(shí)例,則拋出異常或返回已存在的實(shí)例。這樣,在通過(guò)反射調(diào)用私有構(gòu)造函數(shù)時(shí),就會(huì)拋出異?;蚍祷匾汛嬖诘膶?shí)例,從而阻止破解單例模式。

package com.yanyu.Singleton;


public class Singleton {
    private static boolean flag = false;
    //私有構(gòu)造方法
    private Singleton() {
        /*
           反射破解單例模式需要添加的代碼
        */
        synchronized (Singleton.class){
            if (flag){
                throw new RuntimeException("不能創(chuàng)建多個(gè)對(duì)象");
            }
            flag = true;
        }
    }

    private static volatile Singleton instance;
    //對(duì)外提供靜態(tài)方法獲取該對(duì)象
    public static Singleton getInstance() {
        if(instance != null) {
            return instance;
        }
        synchronized (Singleton.class) {
            if(instance != null) {
                return instance;
            }
            instance = new Singleton();
            return instance;
        }
    }
}

上述代碼中的防止反射破解的代碼可以阻止通過(guò)反射調(diào)用私有構(gòu)造方法創(chuàng)建多個(gè)實(shí)例。但是,如果通過(guò)反射修改了flag標(biāo)志位,就可以繞過(guò)這個(gè)限制,破壞單例模式。

為了進(jìn)一步防止通過(guò)反射破解單例模式,可以在getInstance方法中添加判斷,如果已存在實(shí)例,再次調(diào)用構(gòu)造方法時(shí),直接返回已存在的實(shí)例。這樣,即使通過(guò)反射修改了flag標(biāo)志位,也無(wú)法創(chuàng)建新的實(shí)例,從而保證單例模式的唯一性。

?1.6JDK源碼解析-Runtime類

?從上面源代碼中可以看出Runtime類使用的是惡漢式(靜態(tài)屬性)方式來(lái)實(shí)現(xiàn)單例模式的。

使用Runtime類中的方法


public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
        //獲取Runtime類對(duì)象
        Runtime runtime = Runtime.getRuntime();
?
        //返回 Java 虛擬機(jī)中的內(nèi)存總量。
        System.out.println(runtime.totalMemory());
        //返回 Java 虛擬機(jī)試圖使用的最大內(nèi)存量。
        System.out.println(runtime.maxMemory());
?
        //創(chuàng)建一個(gè)新的進(jìn)程執(zhí)行指定的字符串命令,返回進(jìn)程對(duì)象
        Process process = runtime.exec("ipconfig");
        //獲取命令執(zhí)行后的結(jié)果,通過(guò)輸入流獲取
        InputStream inputStream = process.getInputStream();
        byte[] arr = new byte[1024 * 1024* 100];
        int b = inputStream.read(arr);
        System.out.println(new String(arr,0,b,"gbk"));
    }
}

1.7應(yīng)用案例

  • Windows 的 Task Manager(任務(wù)管理器)。
  • Windows 的 Recycle Bin(回收站)。在整個(gè)系統(tǒng)運(yùn)行過(guò)程中,回收站一直維護(hù)著僅有的一個(gè)實(shí)例。
  • 網(wǎng)站的計(jì)數(shù)器,一般也是采用單例模式實(shí)現(xiàn),否則難以同步。
  • 應(yīng)用程序的日志應(yīng)用,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開(kāi)狀態(tài),因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作,否則內(nèi)容不好追加。
  • Web 應(yīng)用的配置對(duì)象的讀取,一般也應(yīng)用單例模式,這個(gè)是由于配置文件是共享的資源。

二、實(shí)驗(yàn)

任務(wù)描述

在企業(yè)網(wǎng)站后臺(tái)系統(tǒng)中,一般會(huì)將網(wǎng)站統(tǒng)計(jì)單元進(jìn)行獨(dú)立設(shè)計(jì),比如登錄人數(shù)的統(tǒng)計(jì)、IP 數(shù)量的計(jì)數(shù)等。在這類需要完成全局統(tǒng)計(jì)的過(guò)程中,就會(huì)用到單例模式,即整個(gè)系統(tǒng)只需要擁有一個(gè)計(jì)數(shù)的全局對(duì)象。

本關(guān)任務(wù):模擬網(wǎng)站登錄,高并發(fā)場(chǎng)景。模擬 10 個(gè)登錄線程,程序輸出登錄總數(shù)。

實(shí)現(xiàn)要點(diǎn)

  1. 在類中添加一個(gè)私有靜態(tài)成員變量用于保存單例實(shí)例。
  2. 聲明一個(gè)公有靜態(tài)構(gòu)建方法用于獲取單例實(shí)例。
  3. 在靜態(tài)方法中實(shí)現(xiàn)"延遲初始化"。 該方法會(huì)在首次被調(diào)用時(shí)創(chuàng)建一個(gè)新對(duì)象, 并將其存儲(chǔ)在靜態(tài)成員變量中。 此后該方法每次被調(diào)用時(shí)都返回該實(shí)例。
  4. 將類的構(gòu)造函數(shù)設(shè)為私有。 類的靜態(tài)方法仍能調(diào)用構(gòu)造函數(shù), 但是其他對(duì)象不能調(diào)用。
  5. 檢查客戶端代碼, 將對(duì)單例的構(gòu)造函數(shù)的調(diào)用替換為對(duì)其靜態(tài)構(gòu)建方法的調(diào)用。

編程要求

本任務(wù)有三個(gè)文件“Login.java”、“Client.java”和“Singleton.java”,在右側(cè)編輯器 Begin-End 內(nèi)補(bǔ)充 Singleton 中代碼,其它文件請(qǐng)閱讀代碼

Singleton.java?

package step1;

import java.util.concurrent.atomic.AtomicLong;

public class Singleton {
    private static Singleton instance;
    //AtomicLong是以原子方式操作long值的類,作用是保證并發(fā)時(shí)線程安全的累加
    private AtomicLong count = new AtomicLong(0);
    /********** Begin *********/
    //此處增加Singleton的構(gòu)造函數(shù)
     
    private Singleton() {
    // 私有化構(gòu)造函數(shù),防止外部實(shí)例化
    }
    /********** End *********/
    public static Singleton GetInstance(){
        /********** Begin *********/
        //考慮線程安全問(wèn)題
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
    }
    return instance;

         /********** End *********/
    }
    public AtomicLong getCount() {
        return count;
    }
    public void setCount() {
        count.addAndGet(1);
    }
}

?Login.java

package step1;
//Runnable 接口由其實(shí)現(xiàn)類來(lái)由線程執(zhí)行對(duì)應(yīng)的實(shí)例。對(duì)于實(shí)現(xiàn)類必須是實(shí)現(xiàn)方法 run
public class Login implements Runnable{
    private String loginname;
    public String getLoginname() {
        return loginname;
    }
    public void setLoginname(String loginname) {
        this.loginname = loginname;
    }
    @Override
    public void run() {
        Singleton lazySingleton =Singleton.GetInstance();
        lazySingleton.setCount();
        /*調(diào)試時(shí)觀察
        System.out.println(getLoginname()+"登錄成功."+lazySingleton);
        */
    }
}

Client.java文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-713513.html

package step1;

public class Client {
    public final static int num = 10;

    public static void main(String[] args) throws InterruptedException {
        
        ///創(chuàng)建10個(gè)線程,模擬10個(gè)用戶登錄
        Thread[] threads = new Thread[num];
        for (int i = 0; i < num; i++) {
            Login login = new Login();
            login.setLoginname("" + String.format("%2s", (i + 1)) + "號(hào)用戶");
            //創(chuàng)建了線程threads[i],并把login對(duì)象(已實(shí)現(xiàn)Runnable接口)放入線程中
            threads[i]=new Thread(login);
            //線程狀態(tài)轉(zhuǎn)換為RUNNABLE
            threads[i].start();
        }
        for (Thread thread : threads) {
            //Client等待線程結(jié)束之后才能繼續(xù)運(yùn)行,防止最后一行的System.out.println提前運(yùn)行
            thread.join();
        }
        System.out.println("網(wǎng)站共有"+Singleton.GetInstance().getCount()+"個(gè)用戶登錄");
    }
}

到了這里,關(guān)于萬(wàn)字解析設(shè)計(jì)模式之單例模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • C#設(shè)計(jì)模式之單例模式

    單例模式(Singleton)保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。 單例模式的結(jié)構(gòu)圖如下所示: 對(duì)一些類來(lái)說(shuō),只有一個(gè)實(shí)例是很重要的。如何才能保證一個(gè)類只有一個(gè)實(shí)例并且這個(gè)實(shí)例易于被訪問(wèn)呢? 基于程序員之間的約定或是利用全局變量嗎? 雖然這樣

    2024年02月03日
    瀏覽(22)
  • Java設(shè)計(jì)模式之單例模式

    Java設(shè)計(jì)模式之單例模式

    定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn) 類型:創(chuàng)建型 想確保任何情況下都絕對(duì)只有一個(gè)實(shí)例 例如:線程池,數(shù)據(jù)庫(kù)連接池一般都為單例模式 單例模式優(yōu)點(diǎn) 在內(nèi)存中只有一個(gè)實(shí)例,減少內(nèi)存開(kāi)銷 可以避免對(duì)資源的多重占用 設(shè)置全局訪問(wèn)點(diǎn),嚴(yán)格控制訪問(wèn)

    2024年02月02日
    瀏覽(56)
  • 設(shè)計(jì)模式之單例模式(懶漢, 餓漢)

    設(shè)計(jì)模式之單例模式(懶漢, 餓漢)

    單例模式是一種常用的軟件設(shè)計(jì)模式, 該模式的主要目的是確保某一個(gè)類在內(nèi)存中只能有一個(gè)實(shí)例對(duì)象, 通過(guò)單例模式的方法創(chuàng)建的類在當(dāng)前進(jìn)程中只有一個(gè)實(shí)例對(duì)象. 常見(jiàn)的單例模式有兩種: 餓漢式, 這里的 “餓” 意義表述不夠清晰, 用 “急” 來(lái)表述意義更加容易聯(lián)想一些

    2024年02月22日
    瀏覽(20)
  • C#--設(shè)計(jì)模式之單例模式

    C#--設(shè)計(jì)模式之單例模式

    單例模式大概是所有設(shè)計(jì)模式中最簡(jiǎn)單的一種,如果在面試時(shí)被問(wèn)及熟悉哪些設(shè)計(jì)模式,你可能第一個(gè)答的就是單例模式。 單例模式的實(shí)現(xiàn)分為兩種: 餓漢式:在靜態(tài)構(gòu)造函數(shù)執(zhí)行時(shí)就立即實(shí)例化。 懶漢式:在程序執(zhí)行過(guò)程中第一次需要時(shí)再實(shí)例化。 兩者有各自適用的場(chǎng)景

    2024年02月14日
    瀏覽(12)
  • 淺談設(shè)計(jì)模式之單例模式

    淺談設(shè)計(jì)模式之單例模式

    單例模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。單例模式指的是 單一的一個(gè)類 ,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,并且保證該 對(duì)象唯一 。該類提供了一種訪問(wèn)其唯一對(duì)象的方法,外部需要調(diào)用該類的對(duì)象可以通過(guò)方法獲取,不需要實(shí)例化類的對(duì)象。 關(guān)鍵點(diǎn): 單例

    2024年02月16日
    瀏覽(20)
  • C++設(shè)計(jì)模式創(chuàng)建型之單例模式

    一、概述 ? ? ? ? 單例模式也稱單態(tài)模式,是一種創(chuàng)建型模式,用于創(chuàng)建只能產(chǎn)生一個(gè)對(duì)象實(shí)例的類。例如,項(xiàng)目中只存在一個(gè)聲音管理系統(tǒng)、一個(gè)配置系統(tǒng)、一個(gè)文件管理系統(tǒng)、一個(gè)日志系統(tǒng)等,甚至如果吧整個(gè)Windows操作系統(tǒng)看成一個(gè)項(xiàng)目,那么其中只存在一個(gè)任務(wù)管理

    2024年02月14日
    瀏覽(28)
  • 【Java 設(shè)計(jì)模式】創(chuàng)建型之單例模式

    【Java 設(shè)計(jì)模式】創(chuàng)建型之單例模式

    在軟件開(kāi)發(fā)中,單例模式是一種常見(jiàn)的設(shè)計(jì)模式, 它確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn) 。單例模式在需要控制某些資源,如數(shù)據(jù)庫(kù)連接池、線程池等共享資源的情況下非常有用。在本文中,我們將介紹 Java 設(shè)計(jì)模式中的單例模式,了解其實(shí)現(xiàn)方式、使用場(chǎng)景

    2024年01月18日
    瀏覽(26)
  • 跟著GPT學(xué)設(shè)計(jì)模式之單例模式

    單例設(shè)計(jì)模式(Singleton Design Pattern)一個(gè)類只允許創(chuàng)建一個(gè)對(duì)象(或者實(shí)例),那這個(gè)類就是一個(gè)單例類,這種設(shè)計(jì)模式就叫作單例設(shè)計(jì)模式,簡(jiǎn)稱單例模式。 單例有幾種經(jīng)典的實(shí)現(xiàn)方式,它們分別是:餓漢式、懶漢式、雙重檢測(cè)、靜態(tài)內(nèi)部類、枚舉。 處理資源訪問(wèn)沖突,

    2024年02月05日
    瀏覽(28)
  • Gof23設(shè)計(jì)模式之單例模式(完整)

    單例模式(Singleton pattern)是Java中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種設(shè)計(jì)模式屬于創(chuàng)建型模型,它提供了一種創(chuàng)建對(duì)象的最佳方式。 這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類提供了一種訪問(wèn)其唯一的對(duì)象的方式,可以直接訪

    2024年02月10日
    瀏覽(26)
  • 深入理解設(shè)計(jì)模式-創(chuàng)建型之單例模式

    如果有些數(shù)據(jù)在系統(tǒng)中應(yīng)該且只能保存一份,那就應(yīng)該設(shè)計(jì)為單例類。 如:配置類:在系統(tǒng)中,我們只有一個(gè)配置文件,當(dāng)配置文件被加載到內(nèi)存之后,應(yīng)該被映射為一個(gè)唯一的【配置實(shí)例】,此時(shí)就可以使用單例,當(dāng)然也可以不用。 全局計(jì)數(shù)器:我們使用一個(gè)全局的計(jì)數(shù)

    2024年02月12日
    瀏覽(24)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包