java枚舉的實現(xiàn)原理

基本使用

首先允悦,所有枚舉類型都有一個基類:java.lang.Enum抽象類衅疙,里面提供了一些基礎屬性和基礎方法莲趣。
枚舉類型不僅可以定義枚舉常量,還可以定義屬性饱溢、構造方法喧伞、普通方法、抽象方法等绩郎,比如潘鲫,我們定義了一個包含成員變量,構造方法以及抽象方法的四則運算操作符枚舉類型:

public enum Operator {
    ADD("+") {
        @Override
        public int calculate(int a, int b) {
            return a + b;
        }
    },
    SUB("-") {
        @Override
        public int calculate(int a, int b) {
            return a - b;
        }
    },
    MUL("*") {
        @Override
        public int calculate(int a, int b) {
            return a * b;
        }
    },
    DIV("/") {
        @Override
        public int calculate(int a, int b) {
            return a / b;
        }
    };

    Operator (String operator) {
        this.operator = operator;
    }

    private String operator;

    public abstract int calculate(int a, int b);

    public String getOperator() {
        return operator;
    }
}

Operator表示枚舉類型肋杖,其中的ADD溉仑、SUB等則是具體的枚舉值,可理解為Operator類型的四個實例對象状植。
這樣看起來浊竟,枚舉類型其實也很好理解,就是一個繼承了Enum類的普通類(根據(jù)后面解析津畸,這個枚舉類型實際上仍是個抽象類)振定,它將其所有的實例對象都寫在了類內部,類外部只能引用這些實例對象而不能重新創(chuàng)建新的實例對象肉拓。(根據(jù)后面解析后频,每個枚舉值都對應一個內部類,是這個內部類的實例對象暖途,而這個內部類繼承了枚舉類)

我們利用反編譯信息繼續(xù)挖掘其內部原理卑惜。

原理分析

使用javap Operator.class指令反編譯上面的枚舉類型Operator,得到的信息如下:

public abstract class Operator extends java.lang.Enum<Operator> {
  public static final Operator ADD;
  public static final Operator SUB;
  public static final Operator MUL;
  public static final Operator DIV;
  public static Operator[] values();
  public static Operator valueOf(java.lang.String);
  public abstract int calculate(int, int);
  public java.lang.String getOperator();
  Operator(java.lang.String, int, java.lang.String, Operator$1);
  static {};
}

從.class文件的反編譯信息中可以得出:

  1. 一個枚舉類型實際上是一個繼承了Enum類的抽象類丧肴,這也就解釋了為什么在類外部無法創(chuàng)建枚舉類型的新的實例對象残揉。
  2. 而具體的枚舉值則是Operator抽象類的靜態(tài)final實例,并且生成Operator.class時也生成了四個Operator$1~4.class文件芋浮,所以枚舉值實際上是繼承了Operator抽象類的內部類Operator1抱环、Operator2壳快、...的對象實例。
  3. 編譯出來的Operator類多出了兩個靜態(tài)方法:values和valueOf镇草,還生成了一個靜態(tài)代碼塊static{};
  4. 構造方法的參數(shù)變成了三個眶痰,多出了前面兩個參數(shù)。這兩個參數(shù)對應的是其父類Enum的構造參數(shù)的兩個屬性值:name和ordinal梯啤。

使用javap -c -v Operator.class查看更詳細的反匯編信息竖伯,其中重要的信息點:

  1. InnerClasses字段,聲明了其中包含了四個內部類Operator$1~4因宇。


    InnerClasses
  2. 靜態(tài)代碼塊的具體執(zhí)行內容如下圖分析:


    靜態(tài)代碼塊

    根據(jù)圖中注釋七婴,靜態(tài)代碼塊中分別創(chuàng)建了四個內部類的實例對象,并分別保存到成員變量ADD察滑,SUB打厘,MUL和DIV中,同時保存到一個新創(chuàng)建的長度為4類型為Operator的數(shù)組中贺辰,最終用編譯器生成的靜態(tài)字段$VALUES指向該數(shù)組户盯。

  3. 編譯器生成的values方法
    該方法是一個公共靜態(tài)方法,可通過調用該方法返回這個枚舉類型的枚舉值數(shù)組饲化,即返回其中聲明的所有實例對象莽鸭。
    該方法實際是調用clone方法來獲取$VALUES字段的值,并把類型強轉為Operatro[]類型返回吃靠。
    實際上并不是真正意義上的clone硫眨,并沒有創(chuàng)建新的實例對象,仍是原有的在靜態(tài)代碼塊中創(chuàng)建的實例對象撩笆。
 public static Operator[] values();
    descriptor: ()[LOperator;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #2                  // Field $VALUES:[LOperator;
         3: invokevirtual #3                  // Method "[LOperator;".clone:()Ljava/lang/Object;
         6: checkcast     #4                  // class "[LOperator;"
         9: areturn
  1. 編譯器生成的valueOf方法
    該方法也是一個公共靜態(tài)方法捺球,可通過調用該方法返回參數(shù)String對應的枚舉值。該方法會調用其父類Enum的valueOf方法夕冲,并把類型強轉為Operator。
  public static Operator valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)LOperator;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #5                  // class Operator
         2: aload_0
         3: invokestatic  #6                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #5                  // class Operator
         9: areturn
  1. 構造方法增加了兩個參數(shù)
    Enum類有一個帶有兩個參數(shù)的構造方法裂逐,name和ordinal是Enum類中為每個枚舉定義的兩個final屬性歹鱼,name表示我們定義的枚舉常量的名稱,ordinal則是枚舉常量對應的一個序號卜高,該序號從0開始弥姻,按照聲明順序賦給每個枚舉常量。
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

Enum類中還定義了clone掺涛、readObject等方法庭敦,其中clone方法為final類型,這兩個方法主要用于保證枚舉類型的不可變性阿趁,不能通過克隆聚请,序列化攻擊來復制枚舉,保證一個枚舉常量只有一個實例涡拘。

小結

枚舉類型本質上就是一個普通類疼电,經(jīng)編譯器處理后嚼锄,枚舉類型實際上是一個繼承了Enum類的抽象類,且編譯器為其添加了values和valueOf兩個方法蔽豺。
每一個枚舉常量實際上是一個繼承了枚舉抽象類的內部類的一個實例對象区丑,所有枚舉常量都是在靜態(tài)代碼塊中初始化,即在類加載期間就初始化修陡。

參考:https://blog.csdn.net/mhmyqn/article/details/48087247

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末沧侥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子魄鸦,更是在濱河造成了極大的恐慌宴杀,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件号杏,死亡現(xiàn)場離奇詭異婴氮,居然都是意外死亡,警方通過查閱死者的電腦和手機盾致,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門主经,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人庭惜,你說我怎么就攤上這事罩驻。” “怎么了护赊?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵惠遏,是天一觀的道長。 經(jīng)常有香客問我骏啰,道長节吮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任判耕,我火速辦了婚禮透绩,結果婚禮上,老公的妹妹穿的比我還像新娘壁熄。我一直安慰自己帚豪,他們只是感情好,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布草丧。 她就那樣靜靜地躺著狸臣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昌执。 梳的紋絲不亂的頭發(fā)上烛亦,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天诈泼,我揣著相機與錄音,去河邊找鬼此洲。 笑死厂汗,一個胖子當著我的面吹牛,可吹牛的內容都是我干的呜师。 我是一名探鬼主播娶桦,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼汁汗!你這毒婦竟也來了衷畦?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤知牌,失蹤者是張志新(化名)和其女友劉穎祈争,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體角寸,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡菩混,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了扁藕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沮峡。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖亿柑,靈堂內的尸體忽然破棺而出邢疙,到底是詐尸還是另有隱情,我是刑警寧澤望薄,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布疟游,位于F島的核電站,受9級特大地震影響痕支,放射性物質發(fā)生泄漏颁虐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一卧须、第九天 我趴在偏房一處隱蔽的房頂上張望聪廉。 院中可真熱鬧,春花似錦故慈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至津辩,卻和暖如春拆撼,著一層夾襖步出監(jiān)牢的瞬間容劳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工闸度, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留竭贩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓莺禁,卻偏偏與公主長得像留量,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哟冬,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348