
?枚舉是JDK1.5中新增加的一種數據類型,它最大的特點就是枚舉數據類型的取值范圍由程序員自己規定,本小節將會講解枚舉的用法以及實現枚舉的原理。
如果希望在程序中表示三種顏色的交通信號燈,可以使用一個整型變量的三個值來表示。例如用1表示紅燈、2表示黃燈、3表示綠燈。但可能有人會使用1-3之外的其他數字對整型變量賦值,這就會導致程序在得到變量的值之后出無法解釋它到底是什么顏色的燈。如何保證每個人都使用規定好的3個值來表示信號燈呢?通過使用枚舉類型就可以輕松的解決這個問題。枚舉是JDK1.5中新增加的一種數據類型,枚舉數據類型的取值范圍由程序員自己規定。當規定好了枚舉的取值范圍后,任何人都不能用這個范圍以外的值來給枚舉變量賦值。?
枚舉用關鍵字enum來表示,它本質上也是一種類,但這種類的對象不是通過new關鍵字創建出來的,而是從程序員規定的眾多枚舉值中選擇的。因此,每一個枚舉值本質上都是一個枚舉對象。程序員只需要從File菜單或右鍵菜單中選擇“New”子菜單,然后在菜單項中選擇“Enum”菜單項即可彈出創建接口的對話框,在對話框中填寫枚舉的名稱并設置其訪問度即可創建出一個枚舉。當創建出枚舉之后,程序員就可以根據需要為它定義枚舉值。下面的【例08_19】展示了如何定義枚舉值來表示交通燈的三種顏色,并且如何在switch結構中以枚舉作為參數。?
(資料圖片)
【例08_19 以枚舉表示交通燈顏色】
TrafficLight.java?
public enum TrafficLight { RED,YELLOW,GREEN ;}
Exam08_19.java?
public class Exam08_19 { public static void main(String[] args) { TrafficLight t1 = TrafficLight.RED;//從枚舉的取值列表中選取其一來創建一個枚舉對象 switch(t1){ case RED: System.out.println("紅燈"); break; case YELLOW: System.out.println("黃燈"); break; case GREEN: System.out.println("綠燈"); break; } }}
【例08_19】中定義了一個枚舉TrafficLight,在這個枚舉中定義了三個值來代表交通信號燈的三種顏色。可以看到:枚舉值的定義過程非常簡單,只需要為每一個枚舉值起一個名稱就可以。按照行業慣例,枚舉值以全大寫字母的方式來命名。當枚舉值被定義好之后,每個枚舉對象都只能從枚舉值列表范圍內選取,否則會引起編譯器報錯。?
在main()方法中創建了一個枚舉對象t1,t1選自列表中的RED。程序中以t1作為switch結構的參數來判斷t1表示哪一種顏色的交通信號燈。此處要提醒各位讀者注意:switch結構的每一個case都直接列出了枚舉值的名稱,前面并沒有加枚舉類型的名稱,例如第一個case后面寫的是RED而不是TrafficLight.RED。?
前文講過,枚舉的本質是類。既然是類,當然就可以在其中定義屬性和方法。但是需要注意:在枚舉中定義的屬性和方法都只能出現在枚舉值列表的下面,如果沒有這樣做都會導致出現語法錯誤。實際上,每一個枚舉本身都會自帶一些方法。這些自帶的方法來自于哪里呢?編譯器在對枚舉的源代碼進行編譯時,看到前面使用了enum關鍵字,就會讓這個枚舉去繼承Enum這個類,因此程序中所定義的每一個枚舉其實都是Enum的子類,而枚舉自帶的那些方法就來自于Enum類。下面來講解一下枚舉中自帶的那些方法是如何使用的。?
name()方法:這個方法沒有參數,它可以返回當前枚舉對象的名稱字符串。?ordinal()方法:枚舉的各個值會組成一個數組,ordinal()方法的作用就是獲得當前枚舉對象在數組中的下標。?compareTo()方法:這是一個有參數的方法,它的參數是另一個同類型的枚舉對象,運行結果是當前對象與參數對象在枚舉值列表中的排位差。?values()方法:這是一個靜態方法,方法沒有參數,方法的返回值是一個枚舉數組,數組中存放著這種枚舉類型所有的值。?valueOf()方法:這也是一個靜態方法,這個方法的作用是通過字符串形式的枚舉值名稱獲得對應的枚舉對象。?下面的【例08_20】展示了如何使用這些枚舉自帶的方法以及它們的運行效果。?
【例08_20 枚舉自帶的方法】
Exam08_20.java?
public class Exam08_20 { public static void main(String[] args) { TrafficLight t1 = TrafficLight.RED; TrafficLight t2 = TrafficLight.GREEN; System.out.println("t1對應的枚舉值名稱是:"+t1.name()); System.out.println("t1對應的枚舉值在數組中的下標是:"+t1. ordinal ()); System.out.println("t1與t3對應的枚舉值排位差是:"+t1.compareTo(t2)); TrafficLight[] values = TrafficLight.values(); System.out.print("TrafficLight的全部枚舉值為:"); for(int i = 0;i【例08_20】的運行結果如圖8-29所示。?
圖8-29 【例08_20】運行結果?
從圖8-29可以看出,t1和t3在枚舉值組成的數組中排位差是-2,這是因為t1對應的枚舉值是RED,而t3對應的枚舉值是GREEN,按照定義的先后順序,RED在枚舉值數組中的下標是0,而GREEN在枚舉值數組中的下標是2,經下標值相減得到它們的排位差是-2。此外,在調用valueOf()方法時還需注意:為該方法傳入的字符串參數必須是某一個枚舉值的名稱,如果傳入“BLUE”這樣的字符串將會出現異常。?
8.7.3枚舉的構造方法
枚舉除了可以定義普通方法外,還可以定義構造方法,通過構造方法可以對枚舉對象的屬性值完成初始化操作。例如,每一種顏色的交通燈亮燈時間并不相同,因此可以為TrafficLight這個枚舉定義一個int型的time屬性來表示每種顏色交通燈亮燈的時間,并且通過構造方法初始化time屬性的值。?
枚舉的構造方法與普通類的構造方法有所不同。首先,枚舉的構造方法必須是私有的,如果程序員沒有在構造方法前面添加private關鍵字,編譯器會自動補充添加。其次,調用構造方法時并不是通過構造方法的名稱來調用的。前文講過,每一個枚舉值本質上都是一個枚舉對象。當程序員把枚舉值列舉出來的時候實際上就是創建了若干枚舉對象。調用枚舉構造方法創建對象的正確形式是:在枚舉值的后面加上括號并傳入參數。如果用無參數的構造方法創建枚舉值,則可以不用在枚舉值后面加括號,例如【例08_19】和【例08_20】中的TrafficLight枚舉在列舉每一個枚舉值時,都沒有在枚舉值后面加括號。下面的【例08_21】展示了如何使用TrafficLight枚舉的帶參數構造方法創建枚舉值。?
【例08_21 用帶參數構造方法創建枚舉值】
TrafficLight.java?
public enum TrafficLight { RED(30),YELLOW(5),GREEN(25) ;//① 調用帶參數構造方法創建枚舉值 int time;//亮燈時間 TrafficLight(){ } TrafficLight(int time){ this.time = time; }}Exam08_21.java?
public class Exam08_21 { public static void main(String[] args) { System.out.println("紅燈亮燈時間:"+TrafficLight.RED.time+"秒"); System.out.println("黃燈亮燈時間:"+TrafficLight.YELLOW.time+"秒"); System.out.println("綠燈亮燈時間:"+TrafficLight.GREEN.time+"秒"); }}【例08_21】對TrafficLight枚舉進行了修改,為它增加了int型的time屬性以及兩個構造方法,當然,按照語法規定這些新增的屬性和構造方法都要定義在所有枚舉值的下面。各位讀者請注意標記為①的那一行代碼,可以看到:創建枚舉值的時候在每個枚舉值的后面都加上了小括號,并在小括號中傳入了參數,這個操作其實就是通過帶參數的構造方法來創建枚舉對象。例如程序中的“RED(30)”就表示創建了一個叫做RED的枚舉對象,在創建這個對象時調用的是帶有int型參數的構造方法,并且為構造方法傳遞的參數值是30。由此可見,枚舉的構造方法并不是直接通過構造方法的名稱調用的,而是通過枚舉值的名稱來調用的,各位讀者一定要牢記這個細節。?
TrafficLight枚舉有參數的構造方法在接收到實際參數后,會用這個參數來初始化time屬性,而每一個枚舉值都是一個枚舉對象,因此枚舉值可以直接調用到time屬性。【例08_21】的運行結果如圖8-30所示。?
圖8-30 【例08_21】運行結果?
從運行結果可以看出:每個枚舉值的time屬性都按照傳入構造方法的參數值完成了初始化。這說明創建枚舉值的時候確實是通過帶有參數的構造方法完成的。?
8.7.4枚舉的繼承問題
前文曾經講過,每一個枚舉實際上都是Enum類的子類。但程序員不可以用自己定義的類來繼承Enum類,這是因為定義一個Enum類的子類,就相當于自己編寫了一個枚舉。但枚舉在用法和實現過程上都有很多特殊要求,而程序員的水平參差不齊,如果允許程序員自己編寫枚舉就可能導致被編寫出的枚舉存在各種漏洞。為了避免出現這種有漏洞的枚舉,Java語言規定程序員只能通過enum關鍵字定義枚舉,這樣編譯器就能以定義enum的特殊語法規則來約束程序員,從而不至于定義出有漏洞的枚舉。?
此外,通過enum關鍵字定義出來的枚舉也不能被繼承,因為如果一個枚舉能夠被繼承,那么它的子枚舉就可以擴展更多的枚舉值,從而導致枚舉值的范圍可以被隨意擴大,這顯然違背了枚舉這種數據類型的設計初衷。為了防止枚舉被繼承,編譯器在編譯枚舉源代碼時會在枚舉前面添加一個final關鍵字。
除此文字版教程外,小伙伴們還可以??點擊這里??觀看我在本站的視頻課程學習Java。