單例模式(一)

使用單例模式的目的,是為了保證一個類只會創(chuàng)建一個對象,以避免產(chǎn)生多個對象消耗資源,或者某個對象本應(yīng)只有一個。

實現(xiàn)單例模式的主要要求有:

  • 私有構(gòu)造方法

  • 提供獲取對象的靜態(tài)方法

  • 確保在對象的唯一性,尤其在多線程的情況下

  • 確保對象在反序列化時不會重新構(gòu)建對象

示例

“餓漢式”

餓漢式會在一開始就創(chuàng)建好單例類的對象。然后提供獲取這個對象的方法。

public class A {
    private static final A a = new A();

    private A() {
    }

    public static A getInstance() {
        return a;
    }
}

“懶漢式”

懶漢式會在需要對象想創(chuàng)建這個對象,由于對象可能在多線程環(huán)境中,因此需要進(jìn)行同步,保證對象不會被多次創(chuàng)建。

示例一:
public class A {
    private static A a = null;

    private A() {
    }

    public static synchronized A getInstance() {
        if (a == null) {
            a = new A();
        }
        return a;
    }
}

這種方式由于synchronized 關(guān)鍵字的存在,在訪問是會進(jìn)行加鎖,導(dǎo)致性能浪費。所以便有了Double Check Lock(DCL)實現(xiàn)單例的方式。

示例二:
public class A {
    private static A a = null;

    private A() {
    }

    public static A getInstance() {
        if (a == null) {
            synchronized (A.class) {
                if (a == null) {
                    a = new A();
                }
            }
        }
        return a;
    }
}

這種方式實現(xiàn)的單例,只會在第一次創(chuàng)建進(jìn)行加鎖,之后在獲取對象時,便不會加鎖,提高了性能。此外還可以使用靜態(tài)內(nèi)部類的方式實現(xiàn)單例。

示例三:
public class A {

    private A() {
    }

    public static A getInstance() {
        return SingletonHolder.a;
    }

    private static class SingletonHolder {
        private static final A a = new A();
    }
}

以上的幾種方式都實現(xiàn)了單例模式所要求的前三條,最后一條在反序列化時依然會重新構(gòu)造對象。所以要在類中加入readResolve私有方法,這個方法是一個可以讓開發(fā)者控制反序列化過程的方法,詳情查看關(guān)于 Java 對象序列化您不知道的 5 件事。以餓漢式為例。

示例四:
public class A implements Serializable {
    private static final long serialVersionUID = 0L;
    private static final A a = new A();

    private A() {
    }

    public static A getInstance() {
        return a;
    }

    private Object readResolve() throws ObjectStreamException {
        return a;
    }
}

除了上面的這種方法之外,還有一種更為簡單的方式實現(xiàn)反序列化且不重新構(gòu)造對象的方法,就是使用枚舉實現(xiàn)單例模式。

示例五:
public enum A {
    INSTANCE;
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class B {
    public static void main(String[] args) {
        A a = A.INSTANCE;
        a.setName("AA");
        System.out.println(a.getName());
    }
}

枚舉類默認(rèn)創(chuàng)建時里是線程安全的,且在反序列化時也是單例。

參考資料:Android源碼設(shè)計模式深度分析 Java 的枚舉類型,關(guān)于 Java 對象序列化您不知道的 5 件事。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容