溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JAVA中的枚舉(一)

發(fā)布時(shí)間:2020-06-15 03:49:42 來源:網(wǎng)絡(luò) 閱讀:669 作者:ciyo_yang 欄目:數(shù)據(jù)庫

在實(shí)際編程中,往往存在著這樣的“數(shù)據(jù)集”,它們的數(shù)值在程序中是穩(wěn)定的,而且“數(shù)據(jù)集”中的元素是有限的。例如星期一到星期日七個(gè)數(shù)據(jù)元素組成了一周的“數(shù)據(jù)集”,春夏秋冬四個(gè)數(shù)據(jù)元素組成了四季的“數(shù)據(jù)集”。在Java中想表示這種數(shù)據(jù)集最容易想到的寫法可能是這樣,我們以表示一周五天的工作日來舉例:

public class WeekDay {
        public static final int MONDAY = 1;
        public static final int TUESDAY = 2;
        public static final int WENSDAY = 3;
        public static final int THURSDAY = 4;
        public static final int FRIDAY = 5;
}

現(xiàn)在,你的類就可以使用像WeekDay.TUESDAY這樣的常量了。但是這里隱藏著一些問題,這些常量是Javaint類型的常量,這意味著該方法可以接受任何int 類型的值,即使它和WeekDay中定義的所有日期都對(duì)應(yīng)不上。因此,您需要檢測(cè)上界和下界,在出現(xiàn)無效值的時(shí)候,可能還要拋出一個(gè)IllegalArgumentException。而且,如果后來又添加另外一個(gè)日期(例如WeekDay.SATURDAY ),那么必須改變所有代碼中的上界,才能接受這個(gè)新值。換句話說,在使用這類帶有整型常量的類時(shí),這個(gè)方案也許可行,但并不是非常有效。

     Joshua Bloch老大這時(shí)站了出來,在他的著作《Effective Java》中提出了在這樣場(chǎng)景下的一種非常好的模式——Type-safe enumeration pattern。這個(gè)模式簡(jiǎn)而言之就是給這樣的常量類一個(gè)私有的構(gòu)造方法,然后聲明一些public static final 的同類型的變量暴露給使用者,例如:

public class WeekDay {
         public static final WeekDay MONDAY = new WeekDay(1);
         public static final WeekDay TUESDAY = new WeekDay(2);
         public static final WeekDay WENSDAY = new WeekDay(3);
         public static final WeekDay THURSDAY = new WeekDay(4);
         public static final WeekDay FRIDAY = new WeekDay(5);
         
         public int getValue(){
                   return value;
         }
         
         private int value;
         
         private WeekDay(int i){
                   this.value = i;
         }
         //other methods...
}

這樣做的好處是你的程序現(xiàn)在接受的不是int類型的數(shù)據(jù)了,而是WeekDay的一個(gè)預(yù)定義好的static final的實(shí)例(WeekDay.TUESDAY等 ),例如:

public void setWeekDay(WeekDay weekDay){...}

  而這樣做也避免了可以隨意向方法中傳遞一個(gè)不合法的int型數(shù)值(例如-1)而造成程序錯(cuò)誤。同時(shí),它還會(huì)帶來其他的一些好處:由于這些枚舉的對(duì)象都是一些類的實(shí)例,所以在里面放一些需要的屬性來存放數(shù)據(jù);又由于他們都是單例的,你可以使用equals方法或是==符號(hào)來比較它們。

Joshua Bloch大大提出的枚舉模式,很好用但是好麻煩啊。如果你用過C/C++或是Pascal這樣的語言的話一定會(huì)對(duì)它們的枚舉類型有印象,例如在C/C++中我們可以這樣定義

enum weekday {
   MONDAY,
   TUESDAY,
   WENSDAY,
   THURSDAY,
   FRIDAY
};

然后在程序中就可以用MONDAY、TUESDAY這些變量了。這樣多方便,但是Java 1.4以前的版本并沒有提供枚舉類型的支持,所以如果你是用JDK 1.4開發(fā)程序的話就只能像Joshua Bloch老大那樣寫了。從java 5.0(代號(hào)為Tiger)開始,這種情況改變了,Java從語言層面支持了枚舉類型。

    枚舉是Tiger的一個(gè)很重要的新特性,它是一種新的類型,允許用常量來表示特定的數(shù)據(jù)片斷,而且全部都以類型安全的形式來表示,它使用“enum”關(guān)鍵字來定義。

    我們先來寫一個(gè)簡(jiǎn)單的枚舉類型的定義:

public enum WeekDay{
         MONDAY, TUESDAY, WENSDAY, THURSDAY, FRIDAY; //最后這個(gè)“;”可寫可不寫。
}

這和類、接口的定義很相像嘛!Tiger中的枚舉類型就是一種使用特殊語法“enum”定義的類。所有的枚舉類型是java.lang.Enum的子類。這是Tiger中新引入的一個(gè)類,它本身并不是枚舉類型,但它定義了所有枚舉類型所共有的行為,如下表:

JAVA中的枚舉(一)

注意:雖然所有的枚舉類型都繼承自java.lang.Enum,但是你不能繞過關(guān)鍵字“enum”而使用直接繼承Enum的方式來定義枚舉類型。編譯器會(huì)提示錯(cuò)誤來阻止你這么做。

    WeekDay中定義的五個(gè)枚舉常量之間使用“,”分割開來。這些常量默認(rèn)都是“public static final”的,所以你就不必再為它們加上“public static final”修飾(編譯器會(huì)提示出錯(cuò)),這也是為什么枚舉常量采用大寫字母來命名的原因。而且每一個(gè)常量都是枚舉類型WeekDay的一個(gè)實(shí)例。你可以通過類似“WeekDay.MONDAY”這種格式來獲取到WeekDay中定義的枚舉常量,也可以采用類似“WeekDay oneDay = WeekDay.MONDAY”的方式為枚舉類型變量賦值(你不能給枚舉類型變量分配除了枚舉常量和null以外的值,編譯器會(huì)提示出錯(cuò))。

    作為枚舉類型實(shí)例的枚舉常量是如何初始化的呢?其實(shí)答案很簡(jiǎn)單,這些枚舉常量都是通過Enum中定義的構(gòu)造函數(shù)進(jìn)行初始化的。

//java.lang.Enum中定義的構(gòu)造函數(shù),
//兩個(gè)參數(shù)分別是定義的枚舉常量名稱以及它所在的次序。
protected Enum(String name, int ordinal) {
                   this.name = name;
                   this.ordinal = ordinal;
}

在初始化的過程中,枚舉常量的次序是按照聲明的順序安排的。第一個(gè)枚舉常量的次序是0,依此累加。

    枚舉類型除了擁有Enum提供的方法以外,還存在著兩個(gè)隱藏著的與具體枚舉類型相關(guān)的靜態(tài)方法——values()和valueOf(String arg0)。方法values()可以獲得包含所有枚舉常量的數(shù)組;方法valueOf是java.lang.Enum中方法valueOf的簡(jiǎn)化版本,你可以通過它,根據(jù)傳遞的名稱來得到當(dāng)前枚舉類型中匹配的枚舉常量。


我們來看一個(gè)枚舉類型使用的小例子。需求中要求可以對(duì)指定的日期進(jìn)行相應(yīng)的信息輸出。對(duì)于這么簡(jiǎn)單的需求,這里就使用枚舉類型來進(jìn)行處理。前面我們已經(jīng)定義好了包含有五個(gè)工作日的枚舉類型。下面的代碼則是進(jìn)行輸出的方法:

/**
         * 根據(jù)日期的不同輸出相應(yīng)的日期信息。
         * @param weekDay     代表不同日期的枚舉常量
         */
         public void printWeekDay(WeekDay weekDay){
            switch(weekDay){
             case MONDAY:
                System.out.println(“Today is Monday!”);
break;
             case TUESDAY: 
                System.out.println(“Today is Tuesday!”);
break;
             case WENSDAY: 
                System.out.println(“Today is Wensday!”);
break;
             case THURSDAY: 
                System.out.println(“Today is hursday!”);
break;      
             case FRIDAY: 
                System.out.println(“Today is Friday!”);
break;      
             default:
                throw new AssertionError("Unexpected value: " + weekDay);
                   }
         }

在Tiger以前,switch操作僅能對(duì)int、short、char和byte進(jìn)行操作。而在Tiger中,switch增加了對(duì)枚舉類型的支持,因?yàn)槊杜e類型僅含有有限個(gè)可以使用整數(shù)代替的枚舉常量,這太適合使用switch語句了!就像上面代碼中那樣,你在swtich表達(dá)式中放置枚舉類型變量,就可以在case標(biāo)示中直接使用枚舉類型中的枚舉常量了。

注意:case標(biāo)示的寫法中沒有枚舉類型前綴,這意味著不能將代碼寫成 case Operation. PLUS,只需將其寫成 case PLUS即可。否則,編譯器會(huì)提示出錯(cuò)信息。

    像上面的例子一樣,雖然你已經(jīng)在case標(biāo)示中窮盡了某個(gè)枚舉類型中的所有枚舉常量,但還是建議你在最后加上default標(biāo)示(就像上面代碼示意的那樣)。因?yàn)槿f一為枚舉類型添加一個(gè)新的枚舉常量,而忘了在switch中添加相應(yīng)的處理,是很難發(fā)現(xiàn)錯(cuò)誤的。

為了更好的支持枚舉類型,java.util中添加了兩個(gè)新類:EnumMap和EnumSet。使用它們可以更高效的操作枚舉類型。下面我一一介紹給你:

    EnumMap是專門為枚舉類型量身定做的Map實(shí)現(xiàn)。雖然使用其它的Map實(shí)現(xiàn)(如HashMap)也能完成枚舉類型實(shí)例到值得映射,但是使用EnumMap會(huì)更加高效:它只能接收同一枚舉類型的實(shí)例作為鍵值,并且由于枚舉類型實(shí)例的數(shù)量相對(duì)固定并且有限,所以EnumMap使用數(shù)組來存放與枚舉類型對(duì)應(yīng)的值。這使得EnumMap的效率非常高。

    提示:EnumMap在內(nèi)部使用枚舉類型的ordinal()得到當(dāng)前實(shí)例的聲明次序,并使用這個(gè)次序維護(hù)枚舉類型實(shí)例對(duì)應(yīng)值在數(shù)組的位置。

    下面是使用EnumMap的一個(gè)代碼示例。枚舉類型DataBaseType里存放了現(xiàn)在支持的所有數(shù)據(jù)庫類型。針對(duì)不同的數(shù)據(jù)庫,一些數(shù)據(jù)庫相關(guān)的方法需要返回不一樣的值,示例中g(shù)etURL就是一個(gè)。

//現(xiàn)支持的數(shù)據(jù)庫類型枚舉類型定義
public enum DataBaseType{
                   MySQL,Oracle,DB2,SQLSERVER
}
 
//某類中定義的獲取數(shù)據(jù)庫URL的方法以及EnumMap的聲明。
……
private EnumMap<DataBaseType ,String> urls =
new EnumMap<DataBaseType ,String>(DataBaseType.class);
                   
public DataBaseInfo(){
         urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample");
         urls.put(DataBaseType.mysql,"jdbc:mysql://localhost/mydb");
         urls.put(DataBaseType.oracle,"jdbc:oracle:thin:@localhost:1521:sample");
         urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=mydb");
}
 
/**
* 根據(jù)不同的數(shù)據(jù)庫類型,返回對(duì)應(yīng)的URL
* @param type     DataBaseType枚舉類新實(shí)例
* @return
*/
public String getURL(DataBaseType type){
         return this.urls.get(type);
}

在實(shí)際使用中,EnumMap對(duì)象urls往往是由外部負(fù)責(zé)整個(gè)應(yīng)用初始化的代碼來填充的。這里為了演示方便,類自己做了內(nèi)容填充。

    像例子中那樣,使用EnumMap可以很方便的為枚舉類型在不同的環(huán)境中綁定到不同的值上。如:例子中g(shù)etURL綁定到URL上,在其它的代碼中可能又被綁定到數(shù)據(jù)庫驅(qū)動(dòng)上去。

    EnumSet是枚舉類型的高性能Set實(shí)現(xiàn)。它要求放入它的枚舉常量必須屬于同一枚舉類型。EnumSet提供了許多工廠方法以便于初始化,見下表:

JAVA中的枚舉(一)

  EnumSet作為Set接口實(shí)現(xiàn),它支持對(duì)包含的枚舉常量的遍歷:

for(Operation op : EnumSet.range(Operation.PLUS , Operation.MULTIPLY)) {

                   doSomeThing(op);

}

java.util.EnumSet和java.util.EnumMap是兩個(gè)枚舉集合。EnumSet保證集合中的元素不重復(fù);EnumMap中的 key是enum類型,而value則可以是任意類型。


轉(zhuǎn)載至:http://blog.csdn.net/shimiso/article/details/5909217

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI