单元素枚举类实现单例模式
本文轉(zhuǎn)載自:點擊打開鏈接
Inspired by?Effective Java.
Singleton模式是在編程實踐中應(yīng)用最廣泛的幾種設(shè)計模式之一。以前知道的,實現(xiàn)單例的方法有兩種(下面的A、B)。剛剛在讀《Effective Java的時候》學(xué)到一種新的更好的方法(E):單元素的枚舉類型。同時通過網(wǎng)上資料也知道了其他兩種方法(C、D)。最后一種在Java中從1.5版本開始支持,其他語言在驗證后說明。
A.餓漢式(類加載的時候就創(chuàng)建實例)。
代碼如下:
public class MaYun {
public static final Mayun instance = new Mayun(); //靜態(tài)的final的MaYun
private MaYun() {
//MaYun誕生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
}
Call:MaYun.instance.splitAlipay();
Feature:可以通過反射機制攻擊;線程安全[多個類加載器除外]。
A+.餓漢變種[推薦]
public class MaYun {
private static Mayun instance = new Mayun();
private static getInstance() {
return instance;
}
private MaYun() {
//MaYun誕生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
}
A++.餓漢變種(類初始化的時候?qū)嵗痠nstance):
public class MaYun {
private MaYun instance = null;
static {
instance = new MaYun();
}
private MaYun() {
//MaYun誕生要做的事情
}
public static MaYun getInstance() {
return this.instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
}
B.懶漢式。
代碼如下:
public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun誕生要做的事情
}
public static MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
}
Call:MaYun.getInstance().splitAlipay();
Feature:延時加載;線程不安全,多線程下不能正常工作;需要額外的工作(Serializable、transient、readResolve())來實現(xiàn)序列化。
B+.懶漢式變種。
public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun誕生要做的事情
}
public static synchronized MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
}
Feature:線程安全;效率比較低,因為需要線程同步的時候比較少。
C.靜態(tài)內(nèi)部類[推薦]。
代碼如下:
public class MaYun {
private static class SigletonHolder {
private static final instance = new MaYun();
}
public static final getInstance() {
return SigletonHolder.instance;
}
private MaYun() {
//MaYun誕生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
Call:MaYun.getInstance().splitAlipay();
Feature:線程安全;延遲加載。
D.雙重校驗鎖[不推薦]。
代碼如下:
public class MaYun {
private volatile static MaYun instance;
private MaYun (){}
public static MaYun getInstance() {
if (instance == null) {
synchronized (MaYun.class) {
if (instance == null) {
instance = new MaYun();
}
}
}
return instance;
}
}
Feature:jdk1.5之后才能正常達到單例效果。
E.編寫一個包含單個元素的枚舉類型[極推薦]。
代碼如下:
public enum MaYun {
himself; //定義一個枚舉的元素,就代表MaYun的一個實例
private String anotherField;
MaYun() {
//MaYun誕生要做的事情
//這個方法也可以去掉。將構(gòu)造時候需要做的事情放在instance賦值的時候:
/** himself = MaYun() {
* //MaYun誕生要做的事情
* }
**/
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo綠眉綠眼的望著。。。”);
}
}
Call:MaYun.himself.splitAlipay();
Feature:從Java1.5開始支持;無償提供序列化機制,絕對防止多次實例化,即使在面對復(fù)雜的序列化或者反射攻擊的時候。
總之,五類:懶漢,惡漢,雙重校驗鎖,靜態(tài)內(nèi)部類,枚舉。
惡漢:因為加載類的時候就創(chuàng)建實例,所以線程安全(多個ClassLoader存在時例外)。缺點是不能延時加載。
懶漢:需要加鎖才能實現(xiàn)多線程同步,但是效率會降低。優(yōu)點是延時加載。
雙重校驗鎖:麻煩,在當(dāng)前Java內(nèi)存模型中不一定都管用,某些平臺和編譯器甚至是錯誤的,因為instance = new MaYun()這種代碼在不同編譯器上的行為和實現(xiàn)方式不可預(yù)知。
靜態(tài)內(nèi)部類:延遲加載,減少內(nèi)存開銷。因為用到的時候才加載,避免了靜態(tài)field在單例類加載時即進入到堆內(nèi)存的permanent代而永遠得不到回收的缺點(大多數(shù)垃圾回收算法是這樣)。
枚舉:很好,不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象。但是失去了類的一些特性,沒有延遲加載,用的人也太少了~~
以后多推廣推廣單元素枚舉這種更好的單例實現(xiàn)方式。在項目中的代碼開始修改實施
下面小demo示范一下,這是只有一個元素的枚舉類,枚舉類里面也可以寫方法。
[java]?view plaincopy
測試類如下:
[java]?view plaincopy
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:521)
at go.derek.Test.main(Test.java:13)
可見,不能通過反射來創(chuàng)建枚舉對象,所以這種單例模式可以抵御惡意客戶端通過反射的攻擊。此外,枚舉類的單例模式也不必擔(dān)心反序列化的時候多次創(chuàng)建實例。
總結(jié)
以上是生活随笔為你收集整理的单元素枚举类实现单例模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 下载 Chrome插件 crx的教程
- 下一篇: WDS+MDT部署系统