枚舉類型很早就出現在編程語言中,它們用於將壹組相似的值包含到壹個類型中。該枚舉類型的名稱將被定義為唯壹的類型描述符,這類似於常量的定義。但是,與常量類型相比,枚舉類型可以為聲明的變量提供更大範圍的值。
例如,如果妳想給彩虹塗上七種顏色,妳可以通過在Java程序中定義常量來實現。
清單1。常數定義
公?靜電?班級?RainbowColor?{?
//?恒定定義七種顏色:紅、橙、黃、綠、青、藍、紫。
公共?靜電?決賽?int?紅色?=?0;?
公共?靜電?決賽?int?橙色?=?1;?
公共?靜電?決賽?int?黃色?=?2;?
公共?靜電?決賽?int?綠色?=?3;?
公共?靜電?決賽?int?青色?=?4;?
公共?靜電?決賽?int?藍色?=?5;?
公共?靜電?決賽?int?紫色?=?6;?
}使用時,可以在程序中直接引用這些常量。然而,這種方式仍然存在壹些問題。
類型不安全。
因為顏色常量對應的值是壹個整數,所以在程序執行過程中很有可能會將任意壹個整數值傳入顏色變量,從而導致錯誤。
沒有命名空間。
由於顏色常量只是類的屬性,所以在使用它們時,必須通過類來訪問它們。
壹致性差
因為整數枚舉是壹個編譯時常量,所以編譯過程完成後,客戶端和服務器引用的所有地方都會直接把整數值寫入。這樣,當妳修改舊的枚舉整數值或者添加新的枚舉值時,所有引用的本地代碼都需要重新編譯,否則運行時會出錯。
類型不確定性
由於顏色枚舉值只是無意義的整數值,如果在運行時調試,會發現日誌中有很多幻數,但除了程序員自己,其他人很難理解其中的奧妙。
如何定義枚舉類型
為了改善Java語言在這方面的不足,彌補缺陷,SDK 5.0版本發布時,在語言層面增加了枚舉類型。枚舉類型的定義也很簡單,使用enum關鍵字和名字和大括號括起來的枚舉值體就可以了。例如,上面提到的彩虹顏色可以用新的枚舉方式重新定義:
enum?RainbowColor?{?紅色?橘子,?黃色的?綠色?青色,?藍色的?紫色?}從上面的定義形式來看,似乎Java中的枚舉類型很簡單,但實際上Java語言規範賦予了枚舉類型非常強大的功能。它不僅簡單地將整數值轉換為對象,還將枚舉類型定義轉換為功能齊全的類定義。類型定義的這種擴展允許開發人員向枚舉類型添加任何方法和屬性,並且還可以實現任何接口。此外,Java平臺還為Enum類型提供了高質量的實現,比如Comparable和Serializable接口的默認實現,這樣開發人員壹般不用關心這些細節。
回到本文的主題,枚舉類型的引入能給我們的開發帶來什麽好處?最直接的好處之壹就是擴大了switch語句的使用範圍。在5.0之前,Java中switch的值只能是簡單類型,比如int,byte,short,char。枚舉類型後,可以使用對象。這樣,程序的控制選擇變得更加方便,參見下面的例子:
清單2。定義枚舉類型
//?定義壹周七天的枚舉類型。
公共?enum?WeekDayEnum?{?媽媽?星期二?星期三?周四?Fri?Sat,?孫?}?
//?閱讀當天的信息
WeekDayEnum?今天嗎?=?readToday();?
//?根據日期選擇活動。
切換(今天)?{?
周壹:?做什麽?東西;?打破;?
周二:?做什麽?東西;?打破;?
周三:?做什麽?東西;?打破;?
周四:?做什麽?東西;?打破;?
Fri:?做什麽?東西;?打破;?
Sat:?玩?體育?遊戲;?打破;?
孫:?有嗎?答?休息;?打破;?
對於這些枚舉的日期,JVM將在運行時構造壹個簡單的壹對壹對應的對象實例。這些對象都有唯壹的標識,類似於整數值,switch語句據此執行跳轉。
如何自定義枚舉類型
除了上面最常見的枚舉定義,如果需要給枚舉類型添加壹些復雜的函數,也可以通過類似class的定義自定義枚舉。例如,要向枚舉類型添加屬性,可以按如下方式定義它們:
清單3。自定義枚舉類型
//?定義?RSS(真的?簡單?辛迪加)?種子的枚舉類型
公共?enum?新聞供稿?{?
//?雅虎頭條?RSS?種子
YAHOO _ TOP _ STORIES("/RSS/topstories "),?
//CBS?頭條?RSS?種子
CBS_TOP_STORIES("/CBSNewsMain?format=xml "),?
//?洛杉磯時報頭條?RSS?種子
LATIMES _ TOP _ STORIES("/LATIMES/news?format = XML ");?
//?枚舉對象?RSS?地址屬性
私人?字符串?rss _ url?
//?枚舉對象構造函數
私人?NewsRSSFeedEnum(字符串?rss)?{?
this.rss_url?=?rss?
}?
//?枚舉對象獲取?RSS?地址方法
公共?字符串?getRssURL()?{?
回歸?this.rss _ url?
}?
}上面頭條新聞的枚舉類型,增加了壹個RSS地址的屬性,記錄頭條新聞的訪問地址。同時RSS訪問地址的值需要對外傳入,所以需要定義壹個構造函數來初始化這個屬性。此外,您需要提供讀取RSS地址的方法。
如何避免誤用枚舉
但是,使用Enum時有幾點需要註意:
枚舉類型不支持public和protected修飾符的構造函數,因此構造函數必須是私有的或友好的。因此,枚舉對象不能通過在程序中直接調用其構造函數來初始化。
定義枚舉類型時,如果是簡單類型,那麽最後壹個枚舉值後面不需要跟任何符號;但是如果有自定義方法,那麽最後壹個枚舉值和下面的代碼應該使用分號“;”分開,沒有逗號或空格。
因為枚舉類型的值實際上是通過在運行時構造對象來表示的,所以在集群環境中,每個虛擬機都會構造壹個同義的枚舉對象。所以需要註意的是,如果直接使用等號(' = = ')運算符,這些看似相同的枚舉值壹定是不相等的,因為它們不是同壹個對象實例。
請看下面的例子:
清單4。避免誤用枚舉示例
//?定義壹周七天的枚舉類型。
包裹?示例.枚舉.代碼;?
公共?enum?WeekDayEnum?{?
Mon(1),?周二(2),?Wed(3),?Thu(4),?Fri(5),?Sat(6),?孫(7);?
私人?int?指數;?
WeekDayEnum(int?idx)?{?
this.index?=?idx?
}?
公共?int?getIndex()?{?
回歸?指數;?
}?
}//?客戶端程序通過網絡向服務器發送枚舉值。
包裹?示例.枚舉.代碼;?
進口?Java . io . io exception;?
進口?Java . io . object output stream;?
進口?Java . io . output stream;?
進口?Java . net . inetsocketaddress;?
進口?Java . net . socket;?
進口?Java . net . unknown host exception;?
公共?班級?枚舉客戶端?{?
公共?靜電?作廢?主(字符串...?args)?摔投?未知主機異常,?IOException?{?
插座?插座?=?新的?socket();?
//?建立與服務器的連接。
socket.connect(新?InetSocketAddress(" 127 . 0 . 0 . 1 ",8999));?
//?從連接中獲取輸出流
OutputStream?os?=?socket . get output stream();?
ObjectOutputStream?oos?=?新的?ObjectOutputStream(OS);?
//?將Friday的枚舉值傳遞給服務器。
oos.writeObject(WeekDayEnum。Fri);?
OOS . close();?
OS . close();?
socket . close();?
}?
}?
//?壹個服務器端程序,將從客戶端接收的枚舉值應用於邏輯處理。
包裹?示例.枚舉.代碼;?
進口?Java . io . *;?
進口?Java . net . server socket;?
進口?Java . net . socket;?
公共?班級?EnumerationServer?{?
公共?靜電?作廢?主(字符串...?args)?摔投?IOException,?ClassNotFoundException?{?
ServerSocket?服務器?=?新的?server socket(8999);?
//?建立服務器端網絡連接攔截
插座?插座?=?server . accept();?
//?從連接中獲取輸入流。
InputStream?是嗎?=?socket . getinputstream();?
ObjectInputStream?ois?=?新的?ObjectInputStream(是);?
//?讀取客戶端傳遞的枚舉值。
WeekDayEnum?日?=?(WeekDayEnum)?ois . read object();?
//?通過值比較來比較枚舉對象。
如果?(日?==?工作日枚舉。Fri)?{?
System.out.println("client?星期五?enum?價值?是嗎?壹樣?作為?服務器的”);?
}?不然呢?如果?(day.equals(WeekDayEnum。Fri)?{?
System.out.println("client?星期五?enum?價值?是嗎?平等?去哪?服務器的”);?
}?不然呢?{?
System.out.println("client?星期五?enum?價值?是嗎?不是嗎?壹樣?作為?服務器的”);?
}?
//?使用?切換?方法來比較枚舉對象。
切換?(天)?{?
案子?周壹:?
System.out.println("Do?星期壹?工作”);?
打破;?
案子?周二:?
System.out.println("Do?星期二?工作”);?
打破;?
案子?周三:?
System.out.println("Do?星期三?工作”);?
打破;?
案子?周四:?
System.out.println("Do?周四?工作”);?
打破;?
案子?Fri:?
System.out.println("Do?星期五?工作”);?
打破;?
案子?Sat:?
System.out.println("Do?周六?工作”);?
打破;?
案子?孫:?
System.out.println("Do?周日嗎?工作”);?
打破;?
默認:?
System.out.println("I?不要?知道嗎?哪個?是嗎?天”);?
打破;?
}?
ois . close();?
is . close();?
socket . close();?
}?
}打印結果如下:
客戶端星期五枚舉值與服務器相同
周五工作
通過程序執行的結果可以發現,在分布式的情況下,客戶端和服務器端的虛擬機上都生成了壹個枚舉對象。即使Fri枚舉值看起來壹樣,如果用等號' = = '來比較,也會有不相等。switch語句通過equal方法比較枚舉對象的值,因此當您的枚舉對象很復雜時,您需要小心與覆蓋相關的方法,以防止值比較中的錯誤。
枚舉相關工具類
在JDK5.0中,在添加Enum類的同時,還添加了EnumSet和EnumMap兩個工具類,這兩個工具類都放在java.util包中。EnumSet是枚舉類型的高性能Set接口實現。EnumSet中加載的所有枚舉對象必須是同壹類型,並且是通過位向量實現的,也就是通過壹個長數字實現的。EnumSet支持遍歷枚舉類型的所有值。回到上面的日期枚舉示例:
enum?WeekDayEnum?{?媽媽?星期二?星期三?周四?Fri?Sat,?孫?}您可以壹周叠代七天,EnumSet類提供了壹個靜態方法範圍,使叠代易於完成:
for(WeekDayEnum?日?:?EnumSet.range(WeekDayEnum。媽媽?工作日枚舉。Fri)?{?
System.out.println(日);?
}打印結果如下:
蒙?
周二?
婚禮?
Thu?
FriEnumSet還提供了許多類型的獲取子集的安全方法,這使您可以輕松地獲取子集:
EnumSet & ltWeekDayEnum & gt?子集?=?EnumSet.of(WeekDayEnum。媽媽?工作日枚舉。wed);?
為了什麽?(WeekDayEnum?日?:?子集)?{?
System.out.println(日);
}打印結果如下:
蒙?
Wed類似於EnumSet,EnumMap也是壹個高性能的Map接口實現,用來管理以枚舉類型為鍵的映射表,在內部通過數組實現。EnumMap結合了豐富而安全的地圖界面和對陣列的快速訪問。如果要將枚舉類型映射到值,應該使用EnumMap。請看下面的例子:
清單5。EnumMap示例
//?定義壹個?EnumMap?對象,映射表主鍵是日期枚舉類型,值是顏色枚舉類型。
私人?靜電?地圖& ltWeekDayEnum,?RainbowColor & gt?圖式?=?
新的?EnumMap & ltWeekDayEnum,?RainbowColor & gt(weekdayenum . class);?
靜態{?
//?用某種顏色的彩虹繪制壹周中的每壹天。
為了什麽?(int?我?=?0;?我?& lt?WeekDayEnum.values()。長度;?i++)?{?
schema . put(weekday enum . values()[I],?rainbow color . values()[I]);?
}?
}?
System.out.println("什麽?是嗎?那個?幸運嗎?顏色?今天?”);?
System.out.println("是嗎?"?+?schema.get(WeekDayEnum。sat));當妳問星期六的幸運顏色時,妳會得到藍色:
清單6。運行結果
今天的幸運色是什麽?
它是藍色的
結束語
Enum類型給JAVA編程帶來了極大的便利,使得程序的控制更加容易,不容易出錯。所以當妳需要控制程序流程的時候,可以多想想是否可以用enum來實現。