关于对Enum的理解
之前一直對枚舉類型的理解存在誤解,現(xiàn)重新學習
Enum 類型的介紹
枚舉類型(Enumerated Type) 很早就出現(xiàn)在編程語言中,它被用來將一組類似的值包含到一種類型當中。而這種枚舉類型的名稱則會被定義成獨一無二的類型描述符,在這一點上和常量的定義相似。不過相比較常量類型,枚舉類型可以為申明的變量提供更大的取值范圍。
舉個例子來說明一下,如果希望為彩虹描繪出七種顏色,你可以在?Java?程序中通過常量定義方式來實現(xiàn)。
清單 1. 常量定義
Public static class RainbowColor { // 紅橙黃綠青藍紫七種顏色的常量定義public static final int RED = 0; public static final int ORANGE = 1; public static final int YELLOW = 2; public static final int GREEN = 3; public static final int CYAN = 4; public static final int BLUE = 5; public static final int PURPLE = 6; }使用的時候,你可以在程序中直接引用這些常量。但是,這種方式還是存在著一些問題。
由于顏色常量的對應值是整數(shù)形,所以程序執(zhí)行過程中很有可能給顏色變量傳入一個任意的整數(shù)值,導致出現(xiàn)錯誤。
由于顏色常量只是類的屬性,當你使用的時候不得不通過類來訪問。
因為整形枚舉屬于編譯期常量,所以編譯過程完成后,所有客戶端和服務器端引用的地方,會直接將整數(shù)值寫入。這樣,當你修改舊的枚舉整數(shù)值后或者增加新的枚舉值后,所有引用地方代碼都需要重新編譯,否則運行時刻就會出現(xiàn)錯誤。
由于顏色枚舉值僅僅是一些無任何含義的整數(shù)值,如果在運行期調(diào)試時候,你就會發(fā)現(xiàn)日志中有很多魔術(shù)數(shù)字,但除了程序員本身,其他人很難明白其奧秘。
如何定義 Enum 類型
為了改進 Java 語言在這方面的不足彌補缺陷,5.0 版本 SDK 發(fā)布時候,在語言層面上增加了枚舉類型。枚舉類型的定義也非常的簡單,用 enum 關(guān)鍵字加上名稱和大括號包含起來的枚舉值體即可,例如上面提到的彩虹顏色就可以用新的 enum 方式來重新定義:
enum RainbowColor { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, PURPLE }從上面的定義形式來看,似乎 Java 中的枚舉類型很簡單,但實際上 Java 語言規(guī)范賦予枚舉類型的功能非常的強大,它不僅是簡單地將整形數(shù)值轉(zhuǎn)換成對象,而是將枚舉類型定義轉(zhuǎn)變成一個完整功能的類定義。這種類型定義的擴展允許開發(fā)者給枚舉類型增加任何方法和屬性,也可以實現(xiàn)任意的接口。另外,Java 平臺也為 Enum 類型提供了高質(zhì)量的實現(xiàn),比如默認實現(xiàn) Comparable 和 Serializable 接口,讓開發(fā)者一般情況下不用關(guān)心這些細節(jié)。
回到本文的主題上來,引入枚舉類型到底能夠給我們開發(fā)帶來什么樣好處呢?一個最直接的益處就是擴大 switch 語句使用范圍。5.0 之前,Java 中 switch 的值只能夠是簡單類型,比如 int、byte、short、char, 有了枚舉類型之后,就可以使用對象了。這樣一來,程序的控制選擇就變得更加的方便,看下面的例子:
清單 2. 定義 Enum 類型
// 定義一周七天的枚舉類型 public enum WeekDayEnum { Mon, Tue, Wed, Thu, Fri, Sat, Sun } // 讀取當天的信息WeekDayEnum today = readToday(); // 根據(jù)日期來選擇進行活動switch(today) { Mon: do something; break; Tue: do something; break; Wed: do something; break; Thu: do something; break; Fri: do something; break; Sat: play sports game; break; Sun: have a rest; break; }
對于這些枚舉的日期,JVM 都會在運行期構(gòu)造成出一個簡單的對象實例一一對應。這些對象都有唯一的 identity,類似整形數(shù)值一樣,switch 語句就根據(jù)此來進行執(zhí)行跳轉(zhuǎn)。
如何定制 Enum 類型
除了以上這種最常見的枚舉定義形式外,如果需要給枚舉類型增加一些復雜功能,也可以通過類似 class 的定義來給枚舉進行定制。比如要給 enum 類型增加屬性,可以像下面這樣定義:
清單 3. 定制枚舉類型
// 定義 RSS(Really Simple Syndication) 種子的枚舉類型public enum NewsRSSFeedEnum { // 雅虎頭條新聞 RSS 種子YAHOO_TOP_STORIES("http://rss.news.yahoo.com/rss/topstories"), //CBS 頭條新聞 RSS 種子CBS_TOP_STORIES("http://feeds.cbsnews.com/CBSNewsMain?format=xml"), // 洛杉磯時報頭條新聞 RSS 種子LATIMES_TOP_STORIES("http://feeds.latimes.com/latimes/news?format=xml"); // 枚舉對象的 RSS 地址的屬性private String rss_url; // 枚舉對象構(gòu)造函數(shù)private NewsRSSFeedEnum(String rss) { this.rss_url = rss; } // 枚舉對象獲取 RSS 地址的方法@Overridepublic String getRssURL() { return this.rss_url; } }
上面頭條新聞的枚舉類型增加了一個 RSS 地址的屬性 , 記錄頭條新聞的訪問地址。同時,需要外部傳入 RSS 訪問地址的值,因而需要定義一個構(gòu)造函數(shù)來初始化此屬性。另外,還需要向外提供方法來讀取 RSS 地址。
如何避免錯誤使用 Enum
不過在使用 Enum 時候有幾個地方需要注意:
看下面的這個例子:
1 package com.csdn.myEnum; 2 3 import java.util.EnumMap; 4 import java.util.EnumSet; 5 6 public class LightTest { 7 8 // 1. 定義枚舉類型 9 public enum Light { 10 // 利用構(gòu)造函數(shù)傳參 11 RED (1), GREEN (3), YELLOW (2); 12 13 // 定義私有變量 14 private int nCode ; 15 16 // 構(gòu)造函數(shù),枚舉類型只能為私有 17 private Light( int _nCode) { 18 this . nCode = _nCode; 19 } 20 21 @Override 22 public String toString() { 23 return String.valueOf ( this . nCode ); 24 } 25 } 26 27 /** 28 * @param args 29 */ 30 public static void main(String[] args ) { 31 32 // 1. 遍歷枚舉類型 33 System. out .println( " 演示枚舉類型的遍歷 ......" ); 34 testTraversalEnum (); 35 36 // 2. 演示 EnumMap 對象的使用 37 System. out .println( " 演示 EnmuMap 對象的使用和遍歷 ....." ); 38 testEnumMap (); 39 40 // 3. 演示 EnmuSet 的使用 41 System. out .println( " 演示 EnmuSet 對象的使用和遍歷 ....." ); 42 testEnumSet (); 43 } 44 45 /** 46 * 演示枚舉類型的遍歷 47 */ 48 private static void testTraversalEnum() { 49 Light[] allLight = Light.values (); 50 for (Light aLight : allLight) { 51 System. out .println( " 當前燈 name : " + aLight.name()); 52 System. out .println( " 當前燈 ordinal : " + aLight.ordinal()); 53 System. out .println( " 當前燈: " + aLight); 54 } 55 } 56 57 /** 58 * 演示 EnumMap 的使用, EnumMap 跟 HashMap 的使用差不多,只不過 key 要是枚舉類型 59 */ 60 private static void testEnumMap() { 61 // 1. 演示定義 EnumMap 對象, EnumMap 對象的構(gòu)造函數(shù)需要參數(shù)傳入 , 默認是 key 的類的類型 62 EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>( 63 Light. class ); 64 currEnumMap.put(Light. RED , " 紅燈 " ); 65 currEnumMap.put(Light. GREEN , " 綠燈 " ); 66 currEnumMap.put(Light. YELLOW , " 黃燈 " ); 67 68 // 2. 遍歷對象 69 for (Light aLight : Light.values ()) { 70 System. out .println( "[key=" + aLight.name() + ",value=" 71 + currEnumMap.get(aLight) + "]" ); 72 } 73 } 74 75 /** 76 * 演示 EnumSet 如何使用, EnumSet 是一個抽象類,獲取一個類型的枚舉類型內(nèi)容 <BR/> 77 * 可以使用 allOf 方法 78 */ 79 private static void testEnumSet() { 80 EnumSet<Light> currEnumSet = EnumSet.allOf (Light. class ); 81 for (Light aLightSetElement : currEnumSet) { 82 System. out .println( " 當前 EnumSet 中數(shù)據(jù)為: " + aLightSetElement); 83 } 84 85 } 86 }執(zhí)行結(jié)果如下:
?
| 演示枚舉類型的遍歷?...... 當前燈?name?:?RED 當前燈?ordinal?:?0 當前燈:?1 當前燈?name?:?GREEN 當前燈?ordinal?:?1 當前燈:?3 當前燈?name?:?YELLOW 當前燈?ordinal?:?2 當前燈:?2 演示?EnmuMap?對象的使用和遍歷?..... [key=RED,value=?紅燈?] [key=GREEN,value=?綠燈?] [key=YELLOW,value=?黃燈?] 演示?EnmuSet?對象的使用和遍歷?..... 當前?EnumSet?中數(shù)據(jù)為:?1 當前?EnumSet?中數(shù)據(jù)為:?3 當前?EnumSet?中數(shù)據(jù)為:?2 |
四、???通常定義常量方法和枚舉定義常量方法區(qū)別
以下內(nèi)容可能有些無聊,但絕對值得一窺
1.????代碼:
public class State {
public static final int ON = 1;
public static final Int OFF= 0;
}
?
有什么不好了,大家都這樣用了很長時間了,沒什么問題啊。
首先,它不是類型安全的。你必須確保是int
其次,你還要確保它的范圍是0?和1
最后,很多時候你打印出來的時候,你只看到?1?和0?,
但其沒有看到代碼的人并不知道你的企圖,拋棄你所有舊的public static final?常量吧
2.????可以創(chuàng)建一個enum?類,把它看做一個普通的類。除了它不能繼承其他類了。(java?是單繼承,它已經(jīng)繼承了Enum),
可以添加其他方法,覆蓋它本身的方法
3.????switch()?參數(shù)可以使用enum?了
4.????values()?方法是編譯器插入到enum?定義中的static?方法,所以,當你將enum?實例向上轉(zhuǎn)型為父類Enum是,values()?就不可訪問了。解決辦法:在Class?中有一個getEnumConstants()?方法,所以即便Enum?接口中沒有values()?方法,我們?nèi)匀豢梢酝ㄟ^Class?對象取得所有的enum?實例
5.????無法從enum?繼承子類,如果需要擴展enum?中的元素,在一個接口的內(nèi)部,創(chuàng)建實現(xiàn)該接口的枚舉,以此將元素進行分組。達到將枚舉元素進行分組。
6.????使用EnumSet?代替標志。enum?要求其成員都是唯一的,但是enum?中不能刪除添加元素。
7.????EnumMap?的key?是enum?,value?是任何其他Object?對象。
8.????enum?允許程序員為eunm?實例編寫方法。所以可以為每個enum?實例賦予各自不同的行為。
9.????使用enum?的職責鏈(Chain of Responsibility) .?這個關(guān)系到設計模式的職責鏈模式。以多種不同的方法來解決一個問題。然后將他們鏈接在一起。當一個請求到來時,遍歷這個鏈,直到鏈中的某個解決方案能夠處理該請求。
10.???使用enum?的狀態(tài)機?
11.???使用enum?多路分發(fā)
Enum 類型提出給 JAVA 編程帶了了極大的便利,讓程序的控制更加的容易,也不容易出現(xiàn)錯誤。所以在遇到需要控制程序流程時候,可以多想想是否可以利用 enum 來實現(xiàn)。
?
參考資料
學習
- The Java Language Specification,Java 語言規(guī)范描述。
- Enum 類的接口說明,Java 接口文檔描述。
- developerWorks Java 技術(shù)專區(qū):這里有數(shù)百篇關(guān)于 Java 編程各個方面的文章。
附錄自己的代碼(比較前臺輸入的數(shù)據(jù)轉(zhuǎn)換成后臺存儲的數(shù)據(jù)):
import java.util.EnumMap;/*** Created by A0017282 on 2017/2/27.*/ public class EnumDemoOne {public enum Light{衣服(1),包包(2),洗護(3),水果(4),蔬菜(5);private int colorValue;private Light(int colorValue){this.colorValue=colorValue;}public int getColorValue() {return colorValue;}public void setColorValue(int colorValue) {this.colorValue = colorValue;}@Overridepublic String toString(){return String.valueOf(this.colorValue);}}public static void main(String[] args ) {Light[] allLight = Light.values();String testName = "包包";for (Light aLight : allLight){if(aLight.name().equals(testName)){System.out.println(aLight.colorValue);}}} }結(jié)果輸出為:2
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/shuiyelifang/p/6474494.html
總結(jié)
以上是生活随笔為你收集整理的关于对Enum的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shell的if判断比较
- 下一篇: 支持Windows 7的CAD—Auto