你所不知的 java 枚舉

首先給出文章的結(jié)論:

  • 枚舉是類(lèi)類(lèi)型
  • 每個(gè)枚舉常量都是所屬枚舉類(lèi)的對(duì)象
  • 枚舉類(lèi)在加載完成后罐农,無(wú)法再進(jìn)行實(shí)例化操作
  • 枚舉是線程安全的

java 中的枚舉在實(shí)現(xiàn)上非常簡(jiǎn)單趁仙,以下就是一個(gè)枚舉的例子:

public enum FruitEnum {
    APPLE
}

但是如果反編譯 FruitEnum.class耗拓,會(huì)發(fā)現(xiàn)編譯器在背后默默的做了大量的工作兔仰,以下是反編譯結(jié)果:

public final class FruitEnum extends Enum {
    public  static final FruitEnum APPLE;
    private static final FruitEnum $VALUES[];

    private FruitEnum(String s, int i) {
        super(s, i);
    }
  
    public static FruitEnum[] values() {
        return (FruitEnum[])$VALUES.clone();
    }

    public static FruitEnum valueOf(String name) {
        return (FruitEnum)Enum.valueOf(FruitEnum, name);
    }
  
    static {
        APPLE = new FruitEnum("APPLE", 0);
        $VALUES = (new FruitEnum[] {
            APPLE
        });
    }
}

下面根據(jù)反編譯結(jié)果說(shuō)明枚舉的幾個(gè)特性玛臂。

  • 枚舉的實(shí)際類(lèi)型
public final class FruitEnum extends Enum

說(shuō)明枚舉是類(lèi),并且是用 final 修飾的類(lèi)芥映,意味著枚舉不能再被繼承擴(kuò)展溅话。而我們聲明枚舉類(lèi)時(shí)使用的 enum 只是一個(gè)關(guān)鍵字。

其次盅称,所有的枚舉都繼承了一個(gè)基類(lèi) Enum 肩祥,該基類(lèi)為枚舉提供了一些通用方法。

  • 枚舉的構(gòu)造函數(shù)

在 FruitEnum 中缩膝,我們并沒(méi)有定義構(gòu)造函數(shù)混狠,但在反編譯的代碼中,我們發(fā)現(xiàn)編譯器自動(dòng)幫我們添加了以下私有構(gòu)造函數(shù):

private FruitEnum(String s, int i) {
    super(s, i);
}

該私有構(gòu)造函數(shù)只是簡(jiǎn)單調(diào)用了父類(lèi) Enum 的構(gòu)造函數(shù):

protected Enum(String name, int ordinal) {
    this.name = name;       // 枚舉常量的名稱(chēng)
    this.ordinal = ordinal; // 枚舉常量在枚舉類(lèi)的位置疾层,從0開(kāi)始
}

實(shí)際上将饺,編譯器只允許私有的枚舉類(lèi)構(gòu)造函數(shù),并且顯示定義的枚舉構(gòu)造函數(shù)經(jīng)編譯器后云芦,都會(huì)被添加 name 和 ordinal 2個(gè)參數(shù)俯逾,用以調(diào)用父類(lèi) Enum 的構(gòu)造函數(shù)。

  • 枚舉常量

再看反編譯后的 FruitEnum 中的成員變量:

public  static final FruitEnum APPLE;
private static final FruitEnum $VALUES[];

其中 FruitEnum APPLE 對(duì)應(yīng)我們定義的枚舉常量 APPLE舅逸,而 FruitEnum $VALUES[] 則是容納所有枚舉常量的數(shù)組桌肴。

這兩個(gè)變量初始化操作如下:

static {
    APPLE = new FruitEnum("APPLE", 0);
    $VALUES = (new FruitEnum[] { APPLE });
}

可知,每個(gè)枚舉常量都是定義它的類(lèi)的對(duì)象琉历,各枚舉對(duì)象都是用 public static final 修飾的坠七,這也是枚舉對(duì)象被稱(chēng)為枚舉常量的原因水醋。

其次,FruitEnum APPLEFruitEnum $VALUES[] 都是在靜態(tài)代碼塊中進(jìn)行初始化彪置,因此在 JVM 記載完成枚舉類(lèi)后拄踪,各枚舉常量都已被

創(chuàng)建完畢。由于枚舉類(lèi)中只允許出現(xiàn)私有構(gòu)造函數(shù)拳魁,固無(wú)法再實(shí)例化新的枚舉對(duì)象惶桐。

  • 枚舉類(lèi)實(shí)例化

無(wú)法通過(guò)構(gòu)造函數(shù)這種一般手段實(shí)例化枚舉類(lèi),那么通過(guò)反射呢潘懊?

先嘗試以下形式:

Class<FruitEnum> fruitEnumClazz = FruitEnum.class;
FruitEnum fruitDemo1 = fruitEnumClazz.newInstance();

拋出了以下錯(cuò)誤:

因?yàn)?Class.newInstance 內(nèi)部是通過(guò)目標(biāo)Class的 無(wú)參構(gòu)造函數(shù) 創(chuàng)建實(shí)例的姚糊,而上文已經(jīng)說(shuō)了 FruitEnum 的實(shí)際構(gòu)造函數(shù)是:

private FruitEnum(String s, int i) {
    super(s, i);
}

固會(huì)找不到對(duì)應(yīng)的構(gòu)造函數(shù),拋出異常授舟。

換種反射方式救恨,先獲取正確的構(gòu)造函數(shù):

// 獲取 FruitEnum 的構(gòu)造函數(shù)
Constructor<FruitEnum> fruitEnumConstructor = fruitEnumClazz.getDeclaredConstructor(String.class, int.class);
// 獲取訪問(wèn)權(quán)限
fruitEnumConstructor.setAccessible(true);
// 實(shí)例化
FruitEnum fruitDemo2 = fruitEnumConstructor.newInstance();

成功獲取了 FruitEnum 的構(gòu)造函數(shù),然而還是拋出了異常:

在 JVM 層面禁止了通過(guò)反射構(gòu)造枚舉實(shí)例的行為释树。

然而除了反射外肠槽,還可以通過(guò) Object.clone() 方法克隆一個(gè)已存在的對(duì)象。但是這種方式也是不行的奢啥,看 Enum 類(lèi)中的 clone() 方法:

protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

Enum 類(lèi)重寫(xiě)了 Obejct 的 clone()方法秸仙,只要嘗試通過(guò) clone() 方法構(gòu)造枚舉對(duì)象,都會(huì)拋出異常扫尺,并且該方法被設(shè)置為不能再重寫(xiě)筋栋。

  • 單例

從編譯器、JVM正驻,再到 java 內(nèi)部設(shè)計(jì)弊攘,層層把關(guān),封死了實(shí)例化枚舉類(lèi)的這一企圖姑曙。因此有一種作法是通過(guò)枚舉的形式實(shí)現(xiàn)單例襟交,下面給個(gè)示例:

public class EnumSingleton{
    private EnumSingleton() {
      
    }
  
    public static EnumSingleton getInstance(){
        return Singleton.INSTANCE.getInstance();
    }
    
    private static enum Singleton {
        INSTANCE;
        
        private EnumSingleton singleton;
        
        // JVM 保證了此方法絕對(duì)只調(diào)用一次
        private Singleton(){
            singleton = new EnumSingleton();
        }
        
        public EnumSingleton getInstance(){
            return singleton;
        }
    }
}
  • 線程安全

    枚舉常量都是 static 類(lèi)型的,在枚舉類(lèi)加載完成后伤靠,會(huì)進(jìn)行枚舉常量的初始化捣域,之后枚舉類(lèi)無(wú)法再實(shí)例化和修改。java 的類(lèi)加載宴合、初始化過(guò)程是線程安全的焕梅,因此創(chuàng)建一個(gè) enum 是線程安全的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卦洽,一起剝皮案震驚了整個(gè)濱河市贞言,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阀蒂,老刑警劉巖该窗,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弟蚀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡酗失,警方通過(guò)查閱死者的電腦和手機(jī)义钉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)规肴,“玉大人捶闸,你說(shuō)我怎么就攤上這事⊥先校” “怎么了鉴嗤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)序调。 經(jīng)常有香客問(wèn)我,道長(zhǎng)兔簇,這世上最難降的妖魔是什么发绢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮垄琐,結(jié)果婚禮上边酒,老公的妹妹穿的比我還像新娘。我一直安慰自己狸窘,他們只是感情好墩朦,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著翻擒,像睡著了一般氓涣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陋气,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天劳吠,我揣著相機(jī)與錄音,去河邊找鬼巩趁。 笑死痒玩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的议慰。 我是一名探鬼主播蠢古,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼别凹!你這毒婦竟也來(lái)了草讶?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤番川,失蹤者是張志新(化名)和其女友劉穎到涂,沒(méi)想到半個(gè)月后脊框,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡践啄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年浇雹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屿讽。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昭灵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伐谈,到底是詐尸還是另有隱情烂完,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布诵棵,位于F島的核電站抠蚣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏履澳。R本人自食惡果不足惜嘶窄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望距贷。 院中可真熱鬧柄冲,春花似錦、人聲如沸忠蝗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)阁最。三九已至戒祠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間速种,已是汗流浹背得哆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哟旗,地道東北人贩据。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像闸餐,于是被迫代替她去往敵國(guó)和親饱亮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容