一、前言
Java的枚舉類型相對C#來說具有更靈活可配置性,Java的枚舉類型可以攜帶更多的信息。
// C# enum MyColor{ RED = 0, BLUE = 1 } Console.Write(MyColor.RED); // Java enum MyColor{ RED("Hot", 4), BLUE("SAD",8); private String mood; public String getMood{ return mood; } private int index; public int getIndex(){ return index; } private MyColor(String mood, int index){ this.mood = mood; this.index = index; } } System.out.println(MyColor.RED.getMood());
本文將對枚舉類型進行較為詳細的敘述,以便日后查閱。
二、最簡單的用法——常量
/* 定義 */ // 形式1 enum MyColor{ RED,BLUE } // 形式2 enum MyColor{ RED,BLUE; } /* 使用 */ System.out.println(MyColor.RED.name()); // 顯示RED System.out.println(MyColor.RED.ordinal()); // 顯示0 System.out.println(MyColor.BLUE.name()); // 顯示BLUE System.out.println(MyColor.BLUE.ordinal()); // 顯示1
枚舉值的name()會返回枚舉值的字面量,而ordinal()為返回枚舉值的索引,而索引是以枚舉值定義時的位置來確定,并在編譯時設置的。下面我們來看看到底編譯器為我們做了什么?
final class MyColor extends java.lang.Enum<MyCorlor>{
public static final MyColor RED; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static final MyColor BLUE; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM public static MyColor[] values(); flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #1 // Field $VALUES:[LMyColor; 3: invokevirtual #2 // Method "[LMyColor;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[LMyColor;" 9: areturn LineNumberTable: line 1: 0 public static MyClass valueOf(java.lang.String); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: ldc_w #4 // class MyColor 3: aload_0 4: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 7: checkcast #4 // class MyColor 10: areturn LineNumberTable: line 1: 0 static {}; flags: ACC_STATIC Code: stack=4, locals=0, args_size=0 0: new #4 // class MyColor 3: dup 4: ldc #7 // String RED 6: iconst_0 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 10: putstatic #9 // Field RED:LMyColor; 13: new #4 // class MyColor 16: dup 17: ldc #10 // String BLUE 19: iconst_1 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 23: putstatic #11 // Field BLUE:LMyColor; 26: iconst_2 27: anewarray #4 // class MyColor 30: dup 31: iconst_0 32: getstatic #9 // Field RED:LMyColor; 35: aastore 36: dup 37: iconst_1 38: getstatic #11 // Field BLUE:LMyColor; 41: aastore 42: putstatic #1 // Field $VALUES:[LMyColor; 45: return LineNumberTable: line 2: 0 line 1: 26
}
可以看到編譯器將enum MyColor編譯為一個繼承Enum<MyColor>并且帶修飾符final的MyColor類。
而枚舉值RED和BLUE則被編譯為MyColor的類常量,并且在類加載的初始化階段實例化。MyColor默認的構造函數會調用父類Enum<MyColor>的構造函數Enum<E>(String name, int ordinal)來設置私有字段name和ordinal的值。其中iconst_0和iconst_1分別表示將0和1壓棧,invokespecial #8則是調用構造函數Enum<E>(String name, int ordinal)。
0: new #4 // class MyColor 3: dup 4: ldc #7 // String RED 6: iconst_0 // int 0 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 10: putstatic #9 // Field RED:LMyColor; 13: new #4 // class MyColor 16: dup 17: ldc #10 // String BLUE 19: iconst_1 // int 1 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 23: putstatic #11 // Field BLUE:LMyColor;
另外在類加載的初始化階段會生成一個私有的$VALUE數組用于存放常量RED和BLUE,而在調用MyColor.values()返回的正是這個$VALUE數組的復制品。
26: iconst_2 27: anewarray #4 // class MyColor 30: dup 31: iconst_0 32: getstatic #9 // Field RED:LMyColor; 35: aastore 36: dup 37: iconst_1 38: getstatic #11 // Field BLUE:LMyColor; 41: aastore 42: putstatic #1 // Field $VALUES:[LMyColor;
小結:
1. 定義枚舉類型本質上就是在定義帶final修飾符的Enum<E>的子類;
2. 枚舉值本質為第1點所定義的類的類常量;
3. 枚舉值的ordinal值由其定義時的排序決定,并且在編譯時已經被設置好了。
三、枚舉類型的抽象父類Enum<E>
其實我們大多數情況下都是調用父類Enum<E>的方法來操作自定義的枚舉值,下面一起看看父類Enum<E>吧!
1. 它為抽象類且繼承了Comparable<E>和Serializable兩個類。
2. 內含私有字段name和ordinal和對應的公有get方法name()和ordinal()。
3. 重寫了equals方法,通過==比較兩個枚舉值的內存地址來判斷兩者是否相同。
4. 實現compareTo方法,通過比較兩個枚舉值的ordinal值來做判斷。
5. getDeclaringClass方法,用于返回枚舉的Class對象。
四、攜帶更多信息——自定義構造函數
由于枚舉最終被編譯為類,因此我們通過自定義構造函數、自定義字段和方法來讓枚舉值攜帶更多信息
public enum MyColor{ RED("Hot", 2), BLUE("SAD",5); private String mood; private int index; private MyColor(String mood, int index){ this.mood = mood; this.index = index; } }
注意:
1. 自定義的構造函數必須是私有的;
2. 構造函數內不能顯式調用父類的構造函數;
3. RED、BLUE的ordinal值依然是0和1,那么值依然是RED和BLUE。
上述3點規定和結果的原因是編譯器會對我們的自定義構造函數進行加工變為
private MyColor(String name, String ordinal, String mood, int index){ super(name, ordinal); this.mood = mood; this.index = index; }
五、讓相同枚舉類型下的枚舉值具有不同的行為——重寫枚舉值的方法
public enum MyColor{ RED, BLUE(){ @Override public boolean getFlag(){ return false; } }; public boolean getFlag(){ return true; } } // 調用 System.out.println(MyColor.RED.getFlag()); // 顯示true System.out.println(MyColor.BLUE.getFlag()); // 顯示false
可以看到枚舉值RED和BLUE同一個方法具有不同的行為。其實這是通過匿名內部類的方式實現的,BLUE的類型為MyColor$1 extends MyColor,而RED的類型為MyColor。
六、使用接口組織枚舉
public interface Food { enum Coffee implements Food { BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO } enum Dessert implements Food { FRUIT, CAKE, GELATO } }
七、總結
若有紕漏請大家指正,謝謝。
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/4297741.html ^_^肥仔John
八、參考
http://www.tuicool.com/articles/YvQZFf
http://www.cnblogs.com/hemingwang0902/archive/2011/12/29/2306263.html
http://www.cnblogs.com/frankliiu-java/archive/2010/12/07/1898721.html
文章列表