Java枚举enum以及应用:枚举实现单例模式
枚舉作為一個常規的語言概念,一直到Java5才誕生不得不說有點奇怪,以至于到現在為止很多程序員仍然更喜歡用static final的形式去命名常量而不使用,一般情況下,Java程序員用這種方式去實現枚舉:
class EnumByClass{public static final int RED=0;public static final int GREEN=1;public static final int BLUE=2; }這種方式實現的枚舉也叫int枚舉模式,盡管很常用,但是由int實現的枚舉很難保證安全性,即當調用不在枚舉范圍內的數值時,需要額外的維護。另外 ,也不利于查看log和測試。
此時,我們需要開始使用Java的枚舉類型,例如上面的int枚舉模式類如果用enum實現,那么代碼如下:
enum Color{RED,GREEN,BLUE; }上述是將枚舉作為常量集合的簡單用法,實際上,枚舉更多的還是用于switch,也是在這時才能發現枚舉相對于int枚舉模式的好處,這里面舉一個用enum實現switch的例子:
enum Color{RED,GREEN,BLUE; } public class Hello {public static void main(String[] args){Color color=Color.RED;int counter=10;while (counter-->0){switch (color){case RED:System.out.println("Red");color=Color.BLUE;break;case BLUE:System.out.println("Blue");color=Color.GREEN;break;case GREEN:System.out.println("Green");color=Color.RED;break;}}} }如果我們用int枚舉模式的話,誠然可以用一些類似++,——的語法糖,但是也要更多的考慮到安全性的問題。
如果僅此而已,那么枚舉也沒什么單獨拿出來寫博客的價值。
我個人對enum感興趣主要是因為之前在介紹Singleton時有一個非常經驗的枚舉實現的單例,代碼如下:
enum SingletonDemo{INSTANCE;public void otherMethods(){System.out.println("Something");} }簡簡單單的一點代碼就實現了一個線程安全的單例,與其說是寫法鬼斧神工,不如說是恰如其分地應用了enum的性質。
在用enum實現Singleton時我曾介紹過三個特性,自由序列化,線程安全,保證單例。這里我們就要探討一下why的問題。
首先,我們都知道enum是由class實現的,換言之,enum可以實現很多class的內容,包括可以有member和member function,這也是我們可以用enum作為一個類來實現單例的基礎。另外,由于enum是通過繼承了Enum類實現的,enum結構不能夠作為子類繼承其他類,但是可以用來實現接口。此外,enum類也不能夠被繼承,在反編譯中,我們會發現該類是final的。
其次,enum有且僅有private的構造器,防止外部的額外構造,這恰好和單例模式吻合,也為保證單例性做了一個鋪墊。這里展開說下這個private構造器,如果我們不去手寫構造器,則會有一個默認的空參構造器,我們也可以通過給枚舉變量參量來實現類的初始化。這里舉一個例子。
enum Color{RED(1),GREEN(2),BLUE(3);private int code;Color(int code){this.code=code;}public int getCode(){return code;} }需要注意的是,private修飾符對于構造器是可以省略的,但這不代表構造器的權限是默認權限。
目前我們對enum的結構和特性有了初步的了解,接下來探究一下原理層次的特性。
想要了解enum是如何工作的,就要對其進行反編譯。
反編譯后就會發現,使用枚舉其實和使用靜態類內部加載方法原理類似。枚舉會被編譯成如下形式:
public final class T extends Enum{
...
}
其中,Enum是Java提供給編譯器的一個用于繼承的類。枚舉量的實現其實是public static final T 類型的未初始化變量,之后,會在靜態代碼中對枚舉量進行初始化。所以,如果用枚舉去實現一個單例,這樣的加載時間其實有點類似于餓漢模式,并沒有起到lazy-loading的作用。
對于序列化和反序列化,因為每一個枚舉類型和枚舉變量在JVM中都是唯一的,即Java在序列化和反序列化枚舉時做了特殊的規定,枚舉的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法是被編譯器禁用的,因此也不存在實現序列化接口后調用readObject會破壞單例的問題。
對于線程安全方面,類似于普通的餓漢模式,通過在第一次調用時的靜態初始化創建的對象是線程安全的。
因此,選擇枚舉作為Singleton的實現方式,相對于其他方式尤其是類似的餓漢模式主要有以下優點:
1. 代碼簡單
2. 自由序列化
至于lazy-loading,考慮到一般情況不存在調用單例類又不需要實例化單例的情況,所以即便不能做到很好的lazy-loading,也并不是大問題。換言之,除了枚舉這種方案,餓漢模式也在單例設計中廣泛的被應用。例如,Hibernate默認的單例,獲取sessionFactory用的HibernateUtil類建立方式如下:
public class HibernateUtil {private static final SessionFactory ourSessionFactory;static {try {Configuration configuration = new Configuration();configuration.configure();ourSessionFactory = configuration.buildSessionFactory();} catch (Throwable ex) {throw new ExceptionInInitializerError(ex);}}public static Session getSession() throws HibernateException {return ourSessionFactory.openSession();} }這是一個典型的餓漢模式,考慮到這個單例只有一個方法即getSession,顯然這種模式本身就是最優的且簡潔的。這里面由于SessionFactory的創建并不是用系統默認的方式,如果想要用enum去實現反而麻煩且無必要。不過至少說明這樣做也許需要一個解決自由序列化的問題。
轉載于:https://www.cnblogs.com/cielosun/p/6596475.html
總結
以上是生活随笔為你收集整理的Java枚举enum以及应用:枚举实现单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安康高新区投放的哈啰共享电瓶车联系方式
- 下一篇: 常吃五谷杂粮的好处