程式中經常會使用到一些常數,如果這些常數是共用的,在java中可以定義一個類或介面來統一管理常數。其他對象從這些類或介面取用常數。如果需要修改常數則可以從這些類或介面上直接修改,而不用更改程式的其他部分。
使用介面儲存常數:
public interface Usual {public static final int TURN_LEFT=1;public static final int TURN_RIGHT=2;public static final int SHOT=3;}
在使用類儲存常數時,直接在類中進行定義即可
public class Action{ public static final int TURN_LEFT=1; ..............................}
對於簡單的常數設定,以上兩種方法已經夠用了。在DK.1.5引用了枚舉類型,帶來了極大的方便
1 簡單的常數設定
2 獲得編譯時間類型檢查
3 增強代碼的可讀性
枚舉類型的性質:
- 定義枚舉類型其實就是在定義一個類,很多細節是由編譯器搞定,所以從某種程度上,enum關鍵字的作用可以看作是interface和class
- 使用enum定義枚舉類型時,定義出的類型是繼承自java.lang.Enum類,而每個被枚舉的成員其實就是定義的枚舉類型的一個執行個體,他們都預設為final,無法改變常數名設定的值,它們也是public和static的成員,可以通過類名稱直接使用它們。
- 枚舉類型的建構函式不得是public和protected的建構函式,這是為了避免粗心的開發人員直接對枚舉類型進行執行個體化
- 因值而異的類實現方式
- Enum覆蓋了toString方法,預設返回字串本身
- Enum提供了valueOf方法,這個方法和toString方法是相對應的,調用valueOf("name")將會返回enum.name,因此我們在重寫toString方法的時候要注意到這一點,一般來說應該相對應的重寫valueOf方法
枚舉類型的方法
- values()方法:取得所有的枚舉成員執行個體,並以數組方式返回
- ordinal()方法:依枚舉順序得到位置索引,預設從0開始
執行個體示範:
1 獲得編譯類型檢查
定義一個介面
public interface Usual {public static final int TURN_LEFT=1;public static final int TURN_RIGHT=2;public static final int SHOT=3;}
public static void main(String[] args) throws IOException{doAction(Action.TURN_LEFT);}public static void doAction(int action){switch(action){case Usual.TURN_LEFT:System.out.println("向左走");break;case Usual.TURN_RIGHT:System.out.println("向右走");break;case Usual.SHOT:System.out.println("射擊");break;}return;}
這種做法本身是沒錯的,但是doAction方法接受的是int類型的常數,我們是沒有能力阻止開發人員對其輸入規定外的常數,也沒有檢查switch中枚舉的值是不是正確的,因為參數action這是int類型而已,當然可以自行設計一些檢查動作,這需要一些額外的工作。而枚舉可以輕易的解決這些問題
定義一個枚舉類
public enum Action {TURN_LEFT,TURN_RIGHT,SHOT;public String get(){switch(this.ordinal()){case 0:return "向右轉";case 1:return "向左轉";case 2:return "射擊";default:return null;}}}
public static void main(String[] args) throws IOException{doAction(Action.TURN_LEFT);}public static void doAction(Action action){switch(action){case TURN_LEFT:System.out.println("向左走");break;case TURN_RIGHT:System.out.println("向右走");break;case SHOT:System.out.println("射擊");break;}return;}
這裡doAction的參數類型是Action,如果對方法輸入其他類型的變數,編譯器就會報告錯誤。
使用枚舉類型還可以做進一步的檢查,如果在switch中加入了不屬於Action中枚舉的值,編譯器也會報告錯誤
public static void doAction(Action action){switch(action){case TURN_LEFT:System.out.println("向左走");break;case TURN_RIGHT:System.out.println("向右走");break;case SHOT:System.out.println("射擊");break;}case STOP:System.out.println("停止");break;return;}
這是編譯器會顯示錯誤:unqualified enumeration constant name required
case STOP:
^
2 枚舉執行個體的簡單應用
下面這段代碼我是從http://www.cnblogs.com/linjiqin/archive/2011/02/11/1951632.html借鑒而來並做了很大的修改【自己想偷懶些】
/** * 枚舉用法詳解 */public class TestEnum { /** * 普通枚舉 */ public enum ColorEnum { red, green, yellow, blue; public static int length(){ return ColorEnum.values().length; }//定義取得枚舉成員的個數即長度 } public enum SeasonEnum { //註:枚舉寫在最前面,否則編譯出錯 spring, summer, autumn, winter; private final static String position = "test"; public static SeasonEnum getSeason() { if ("test".equals(position)) return spring; else return winter; } } /** * 性別 * * 實現帶有構造器的枚舉 */ public enum Gender{ //通過括弧賦值,而且必須帶有一個參構造器和一個屬性跟方法,否則編譯出錯 //賦值必須都賦值或都不賦值,不能一部分賦值一部分不賦值;如果不賦值則不能寫構造器,賦值編譯也出錯 MAN("MAN"), WOMEN("WOMEN"); private final String value; //構造器預設也只能是private, 從而保證建構函式只能在內部使用 Gender(String value) { this.value = value; } public String getValue() { return value; } } /** * * 實現帶有抽象方法的枚舉 */ public enum OrderState { /** 已取消 */ CANCEL {public String getName(){return "已取消";}}, /** 待審核 */ WAITCONFIRM {public String getName(){return "待審核";}}, /** 等待付款 */ WAITPAYMENT {public String getName(){return "等待付款";}}, /** 正在配貨 */ ADMEASUREPRODUCT {public String getName(){return "正在配貨";}}, /** 等待發貨 */ WAITDELIVER {public String getName(){return "等待發貨";}}, /** 已發貨 */ DELIVERED {public String getName(){return "已發貨";}}, /** 已收貨 */ RECEIVED {public String getName(){return "已收貨";}}; public abstract String getName();//這裡是很重要的 } public static void compareToSeasonEnum(SeasonEnum inputseasonenum){ System.out.println("請輸入"+inputseasonenum); for(SeasonEnum el:SeasonEnum.values()){ System.out.println(el.compareTo(inputseasonenum)); } }//比較SeasonEnum枚舉對象間的順序 public static void main(String[] args) { System.out.println("==============================="); ColorEnum colorEnum = ColorEnum.blue; switch (colorEnum) { case red: System.out.println("color is red"); break; case green: System.out.println("color is green"); break; case yellow: System.out.println("color is yellow"); break; case blue: System.out.println("color is blue"); break; } System.out.println("===============================\n\n"); System.out.println("=====遍曆ColorEnum枚舉中的值===="); for(ColorEnum color : ColorEnum.values()){ System.out.println(color); } System.out.println("===============================\n\n"); System.out.println("====ColorEnum枚舉中的值有"+ColorEnum.length()+"個===="); System.out.println(ColorEnum.red.ordinal());//0 System.out.println(ColorEnum.green.ordinal());//1 System.out.println(ColorEnum.yellow.ordinal());//2 System.out.println(ColorEnum.blue.ordinal());//3 System.out.println(ColorEnum.red.compareTo(ColorEnum.green));//-1 System.out.println("===============================\n\n"); System.out.println("==============================="); System.out.println("季節為" + SeasonEnum.getSeason()); System.out.println("===============================\n\n"); System.out.println("==============================="); for(Gender gender : Gender.values()){ System.out.println(gender.value); } System.out.println("===============================\n\n"); System.out.println("==============================="); for(OrderState order : OrderState.values()){ System.out.println(order.getName()); } System.out.println("===============================\n\n"); System.out.println("========valueOf方法應用========"); //靜態valueOf方法可以讓指定的字串嘗試轉換為枚舉執行個體 compareToSeasonEnum(SeasonEnum.valueOf("winter")); System.out.println("===============================\n\n");
System.out.println("******EnumSet的用法********");
// EnumSet的使用
EnumSet<ColorEnum> stateSet = EnumSet.allOf(ColorEnum.class);
for (ColorEnum s : stateSet) {
System.out.println(s);
}
System.out.println("******EnumSet的用法********"); // EnumSet的使用 EnumSet<ColorEnum> stateSet = EnumSet.allOf(ColorEnum.class); for (ColorEnum s : stateSet) { System.out.println(s); } System.out.println("******EnumMap的用法********"); //EnumMap的使用 EnumMap stateMap = new EnumMap(ColorEnum.class); stateMap.put(ColorEnum.red, "is red"); stateMap.put(ColorEnum.green, "is green"); stateMap.put(ColorEnum.yellow, "is yellow"); stateMap.put(ColorEnum.blue, "is blue"); for (ColorEnum s : ColorEnum.values()) { System.out.println(s.name() + ":" + stateMap.get(s)); } } }
運行結果:
===============================color is blue====================================遍曆ColorEnum枚舉中的值====redgreenyellowblue===================================ColorEnum枚舉中的值有4個====0123-1==============================================================季節為spring==============================================================MANWOMEN==============================================================已取消待審核等待付款正在配貨等待發貨已發貨已收貨=======================================valueOf方法應用========請輸入winter-3-2-10===============================******EnumSet的用法********redgreenyellowblue******EnumMap的用法********red:is redgreen:is greenyellow:is yellowblue:is blue
總結:
拋開具體程式設計語言來看,枚舉所具有的核心功能應該是:
◆型別安全(Type Safety)
◆緊湊有效枚舉數值定義(Compact, Efficient Declaration of Enumerated Values)
◆無縫的和程式其它部分的互動操作(Seamless integration with other language features)
◆啟動並執行高效率(Runtime efficiency)
1.型別安全
枚舉的申明建立了一個新的類型。它不同於其他的已有類型,包括原始類型(整數,浮點數等等)和當前範圍(Scope)內的其它的枚舉類型。當你對函數的參數進行賦值操作的時候,整數類型和枚舉類型是不能互換的(除非是你進行顯式的類型轉換),編譯器將強制這一點。
2. 緊湊有效枚舉數值定義
定義枚舉的程式應該很簡單。
3.無縫的和程式其它部分的互動操作
語言的運算子,如賦值,相等/大於/小於判斷都應該支援枚舉。枚舉還應該支援數組下標以及witch/case語句中用來控制流程程的操作。
4. 啟動並執行高效率
枚舉的運行效率應該和原始類型的整數一樣高。在運行時不應該由於使用了枚舉而導致效能比使用整數有所下降。如果一種語言滿足這四點要求,那麼我們可以說這種語言是真正的支援枚舉。比如前面所說的Pascal,Ada,和C++。很明顯,Java不是。
Java的創始人James Gosling是個資深的C++程式員,他很清楚什麼是枚舉。但似乎他有意的刪除了Java的枚舉能力。其原因我們不得而知。可能是他想強調和鼓勵使用多態性(polymorphism),不鼓勵使用多重分支。而多重分支往往是和枚舉聯合使用的。不管他的初衷如何,我們在Java中仍然需要枚舉。