您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Java枚舉類的語(yǔ)法和具體使用方法”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
在程序設(shè)計(jì)中,有時(shí)會(huì)用到由若干個(gè)有限數(shù)據(jù)元素組成的集合,如一周內(nèi)的星期一到星期日七個(gè)數(shù)據(jù)元素組成的集合,由三種顏色紅、黃、綠組成的集合,一個(gè)工作班組內(nèi)十個(gè)職工組成的集合等等,程序中某個(gè)變量取值僅限于集合中的元素。此時(shí),可將這些數(shù)據(jù)集合定義為枚舉類型。
因此,枚舉類型是某類數(shù)據(jù)可能取值的集合,如一周內(nèi)星期可能取值的集合為: ??{ Sun,Mon,Tue,Wed,Thu,Fri,Sat} ??該集合可定義為描述星期的枚舉類型,該枚舉類型共有七個(gè)元素,因而用枚舉類型定義的枚舉變量只能取集合中的某一元素值。由于枚舉類型是導(dǎo)出數(shù)據(jù)類型,因此,必須先定義枚舉類型,然后再用枚舉類型定義枚舉型變量。 ?
enum <枚舉類型名>   { <枚舉元素表> };      其中:關(guān)鍵詞enum表示定義的是枚舉類型,枚舉類型名由標(biāo)識(shí)符組成,而枚舉元素表由枚舉元素或枚舉常量組成。例如:    enum weekdays   { Sun,Mon,Tue,Wed,Thu,Fri,Sat };   定義了一個(gè)名為 weekdays的枚舉類型,它包含七個(gè)元素:Sun、Mon、Tue、Wed、Thu、Fri、Sat。   
在編譯器編譯程序時(shí),給枚舉類型中的每一個(gè)元素指定一個(gè)整型常量值(也稱為序號(hào)值)。若枚舉類型定義中沒有指定元素的整型常量值,則整型常量值從0開始依次遞增,因此,weekdays枚舉類型的七個(gè)元素Sun、Mon、Tue、Wed、Thu、Fri、Sat對(duì)應(yīng)的整型常量值分別為0、1、2、3、4、5、6。 ??注意:在定義枚舉類型時(shí),也可指定元素對(duì)應(yīng)的整型常量值。
例如,描述邏輯值集合{TRUE、FALSE}的枚舉類型boolean可定義如下: enum boolean   { TRUE=1 ,FALSE=0 }; 該定義規(guī)定:TRUE的值為1,而FALSE的值為0。    而描述顏色集合{red,blue,green,black,white,yellow}的枚舉類型colors可定義如下: enum colors   {red=5,blue=1,green,black,white,yellow};   該定義規(guī)定red為5 ,blue為1,其后元素值從2 開始遞增加1。green、black、white、yellow的值依次為2、3、4、5。   
??此時(shí),整數(shù)5將用于表示二種顏色red與yellow。通常兩個(gè)不同元素取相同的整數(shù)值是沒有意義的。枚舉類型的定義只是定義了一個(gè)新的數(shù)據(jù)類型,只有用枚舉類型定義枚舉變量才能使用這種數(shù)據(jù)類型。
enum 與 class、interface 具有相同地位; 可以繼承多個(gè)接口; 可以擁有構(gòu)造器、成員方法、成員變量; 1.2 枚舉類與普通類不同之處
默認(rèn)繼承 java.lang.Enum 類,所以不能繼承其他父類;其中 java.lang.Enum 類實(shí)現(xiàn)了 java.lang.Serializable 和 java.lang.Comparable 接口;
使用 enum 定義,默認(rèn)使用 final 修飾,因此不能派生子類;
構(gòu)造器默認(rèn)使用 private 修飾,且只能使用 private 修飾;
枚舉類所有實(shí)例必須在第一行給出,默認(rèn)添加 public static final 修飾,否則無(wú)法產(chǎn)生實(shí)例;
這部分內(nèi)容參考https://blog.csdn.net/qq_27093465/article/details/52180865
public class 常量 { } enum Color { Red, Green, Blue, Yellow }
JDK1.6之前的switch語(yǔ)句只支持int,char,enum類型,使用枚舉,能讓我們的代碼可讀性更強(qiáng)。
public static void showColor(Color color) { switch (color) { case Red: System.out.println(color); break; case Blue: System.out.println(color); break; case Yellow: System.out.println(color); break; case Green: System.out.println(color); break; } }
如果打算自定義自己的方法,那么必須在enum實(shí)例序列的最后添加一個(gè)分號(hào)。而且 Java 要求必須先定義 enum 實(shí)例。
enum Color { //每個(gè)顏色都是枚舉類的一個(gè)實(shí)例,并且構(gòu)造方法要和枚舉類的格式相符合。 //如果實(shí)例后面有其他內(nèi)容,實(shí)例序列結(jié)束時(shí)要加分號(hào)。 Red("紅色", 1), Green("綠色", 2), Blue("藍(lán)色", 3), Yellow("黃色", 4); String name; int index; Color(String name, int index) { this.name = name; this.index = index; } public void showAllColors() { //values是Color實(shí)例的數(shù)組,在通過index和name可以獲取對(duì)應(yīng)的值。 for (Color color : Color.values()) { System.out.println(color.index + ":" + color.name); } } }
所有枚舉類都繼承自Enum類,所以可以重寫該類的方法 下面給出一個(gè)toString()方法覆蓋的例子。
@Override public String toString() { return this.index + ":" + this.name; }
所有的枚舉都繼承自java.lang.Enum類。由于Java 不支持多繼承,所以枚舉對(duì)象不能再繼承其他類。
enum Color implements Print{ @Override public void print() { System.out.println(this.name); } }
搞個(gè)實(shí)現(xiàn)接口,來(lái)組織枚舉,簡(jiǎn)單講,就是分類吧。如果大量使用枚舉的話,這么干,在寫代碼的時(shí)候,就很方便調(diào)用啦。
public class 用接口組織枚舉 { public static void main(String[] args) { Food cf = chineseFood.dumpling; Food jf = Food.JapaneseFood.fishpiece; for (Food food : chineseFood.values()) { System.out.println(food); } for (Food food : Food.JapaneseFood.values()) { System.out.println(food); } } } interface Food { enum JapaneseFood implements Food { suse, fishpiece } } enum chineseFood implements Food { dumpling, tofu }
java.util.EnumSet和java.util.EnumMap是兩個(gè)枚舉集合。EnumSet保證集合中的元素不重復(fù);EnumMap中的 key是enum類型,而value則可以是任意類型。
EnumSet在JDK中沒有找到實(shí)現(xiàn)類,這里寫一個(gè)EnumMap的例子
public class 枚舉類集合 { public static void main(String[] args) { EnumMap<Color, String> map = new EnumMap<Color, String>(Color.class); map.put(Color.Blue, "Blue"); map.put(Color.Yellow, "Yellow"); map.put(Color.Red, "Red"); System.out.println(map.get(Color.Red)); } }
cdn.xitu.io/2019/4/7/169f6cd0645e7113?w=1144&h=154&f=png&s=81857">
枚舉類型對(duì)象之間的值比較,是可以使用==,直接來(lái)比較值,是否相等的,不是必須使用equals方法的喲。
因?yàn)槊杜e類Enum已經(jīng)重寫了equals方法
/** * Returns true if the specified object is equal to this * enum constant. * * @param other the object to be compared for equality with this object. * @return true if the specified object is equal to this * enum constant. */ public final boolean equals(Object other) { return this==other; }
這部分參考https://blog.csdn.net/mhmyqn/article/details/48087247
Java從JDK1.5開始支持枚舉,也就是說,Java一開始是不支持枚舉的,就像泛型一樣,都是JDK1.5才加入的新特性。通常一個(gè)特性如果在一開始沒有提供,在語(yǔ)言發(fā)展后期才添加,會(huì)遇到一個(gè)問題,就是向后兼容性的問題。
像Java在1.5中引入的很多特性,為了向后兼容,編譯器會(huì)幫我們寫的源代碼做很多事情,比如泛型為什么會(huì)擦除類型,為什么會(huì)生成橋接方法,foreach迭代,自動(dòng)裝箱/拆箱等,這有個(gè)術(shù)語(yǔ)叫“語(yǔ)法糖”,而編譯器的特殊處理叫“解語(yǔ)法糖”。那么像枚舉也是在JDK1.5中才引入的,又是怎么實(shí)現(xiàn)的呢?
Java在1.5中添加了java.lang.Enum抽象類,它是所有枚舉類型基類。提供了一些基礎(chǔ)屬性和基礎(chǔ)方法。同時(shí),對(duì)把枚舉用作Set和Map也提供了支持,即java.util.EnumSet和java.util.EnumMap。
接下來(lái)定義一個(gè)簡(jiǎn)單的枚舉類
public enum Day { MONDAY { @Override void say() { System.out.println("MONDAY"); } } , TUESDAY { @Override void say() { System.out.println("TUESDAY"); } }, FRIDAY("work"){ @Override void say() { System.out.println("FRIDAY"); } }, SUNDAY("free"){ @Override void say() { System.out.println("SUNDAY"); } }; String work; //沒有構(gòu)造參數(shù)時(shí),每個(gè)實(shí)例可以看做常量。 //使用構(gòu)造參數(shù)時(shí),每個(gè)實(shí)例都會(huì)變得不一樣,可以看做不同的類型,所以編譯后會(huì)生成實(shí)例個(gè)數(shù)對(duì)應(yīng)的class。 private Day(String work) { this.work = work; } private Day() { } //枚舉實(shí)例必須實(shí)現(xiàn)枚舉類中的抽象方法 abstract void say (); }
反編譯結(jié)果
D:\MyTech\out\production\MyTech\com\javase\枚舉類>javap Day.class Compiled from "Day.java" public abstract class com.javase.枚舉類.Day extends java.lang.Enum<com.javase.枚舉類.Day> { public static final com.javase.枚舉類.Day MONDAY; public static final com.javase.枚舉類.Day TUESDAY; public static final com.javase.枚舉類.Day FRIDAY; public static final com.javase.枚舉類.Day SUNDAY; java.lang.String work; public static com.javase.枚舉類.Day[] values(); public static com.javase.枚舉類.Day valueOf(java.lang.String); abstract void say(); com.javase.枚舉類.Day(java.lang.String, int, com.javase.枚舉類.Day$1); com.javase.枚舉類.Day(java.lang.String, int, java.lang.String, com.javase.枚舉類.Day$1); static {}; }
可以看到,一個(gè)枚舉在經(jīng)過編譯器編譯過后,變成了一個(gè)抽象類,它繼承了java.lang.Enum;而枚舉中定義的枚舉常量,變成了相應(yīng)的public static final屬性,而且其類型就抽象類的類型,名字就是枚舉常量的名字.
同時(shí)我們可以在Operator.class的相同路徑下看到四個(gè)內(nèi)部類的.class文件com/mikan/Day$1.class、com/mikan/Day$2.class、com/mikan/Day$3.class、com/mikan/Day$4.class,也就是說這四個(gè)命名字段分別使用了內(nèi)部類來(lái)實(shí)現(xiàn)的;同時(shí)添加了兩個(gè)方法values()和valueOf(String);我們定義的構(gòu)造方法本來(lái)只有一個(gè)參數(shù),但卻變成了三個(gè)參數(shù);同時(shí)還生成了一個(gè)靜態(tài)代碼塊。這些具體的內(nèi)容接下來(lái)仔細(xì)看看。
下面分析一下字節(jié)碼中的各部分,其中:
InnerClasses: static #23; //class com/javase/枚舉類/Day$4 static #18; //class com/javase/枚舉類/Day$3 static #14; //class com/javase/枚舉類/Day$2 static #10; //class com/javase/枚舉類/Day$1
從中可以看到它有4個(gè)內(nèi)部類,這四個(gè)內(nèi)部類的詳細(xì)信息后面會(huì)分析。
static {}; descriptor: ()V flags: ACC_STATIC Code: stack=5, locals=0, args_size=0 0: new #10 // class com/javase/枚舉類/Day$1 3: dup 4: ldc #11 // String MONDAY 6: iconst_0 7: invokespecial #12 // Method com/javase/枚舉類/Day$1."<init>":(Ljava/lang/String;I)V 10: putstatic #13 // Field MONDAY:Lcom/javase/枚舉類/Day; 13: new #14 // class com/javase/枚舉類/Day$2 16: dup 17: ldc #15 // String TUESDAY 19: iconst_1 20: invokespecial #16 // Method com/javase/枚舉類/Day$2."<init>":(Ljava/lang/String;I)V //后面類似,這里省略 }
其實(shí)編譯器生成的這個(gè)靜態(tài)代碼塊做了如下工作:分別設(shè)置生成的四個(gè)公共靜態(tài)常量字段的值,同時(shí)編譯器還生成了一個(gè)靜態(tài)字段$VALUES,保存的是枚舉類型定義的所有枚舉常量 編譯器添加的values方法:
public static com.javase.Day[] values(); flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #2 // Field $VALUES:[Lcom/javase/Day; 3: invokevirtual #3 // Method "[Lcom/mikan/Day;".clone:()Ljava/lang/Object; 6: checkcast #4 // class "[Lcom/javase/Day;" 9: areturn 這個(gè)方法是一個(gè)公共的靜態(tài)方法,所以我們可以直接調(diào)用該方法(Day.values()),返回這個(gè)枚舉值的數(shù)組,另外,這個(gè)方法的實(shí)現(xiàn)是,克隆在靜態(tài)代碼塊中初始化的$VALUES字段的值,并把類型強(qiáng)轉(zhuǎn)成Day[]類型返回。
造方法為什么增加了兩個(gè)參數(shù)?
有一個(gè)問題,構(gòu)造方法我們明明只定義了一個(gè)參數(shù),為什么生成的構(gòu)造方法是三個(gè)參數(shù)呢?
從Enum類中我們可以看到,為每個(gè)枚舉都定義了兩個(gè)屬性,name和ordinal,name表示我們定義的枚舉常量的名稱,如FRIDAY、TUESDAY,而ordinal是一個(gè)順序號(hào),根據(jù)定義的順序分別賦予一個(gè)整形值,從0開始。在枚舉常量初始化時(shí),會(huì)自動(dòng)為初始化這兩個(gè)字段,設(shè)置相應(yīng)的值,所以才在構(gòu)造方法中添加了兩個(gè)參數(shù)。即: 另外三個(gè)枚舉常量生成的內(nèi)部類基本上差不多,這里就不重復(fù)說明了。
我們可以從Enum類的代碼中看到,定義的name和ordinal屬性都是final的,而且大部分方法也都是final的,特別是clone、readObject、writeObject這三個(gè)方法,這三個(gè)方法和枚舉通過靜態(tài)代碼塊來(lái)進(jìn)行初始化一起。
它保證了枚舉類型的不可變性,不能通過克隆,不能通過序列化和反序列化來(lái)復(fù)制枚舉,這能保證一個(gè)枚舉常量只是一個(gè)實(shí)例,即是單例的,所以在effective java中推薦使用枚舉來(lái)實(shí)現(xiàn)單例。
枚舉本質(zhì)上是通過普通的類來(lái)實(shí)現(xiàn)的,只是編譯器為我們進(jìn)行了處理。每個(gè)枚舉類型都繼承自java.lang.Enum,并自動(dòng)添加了values和valueOf方法。
而每個(gè)枚舉常量是一個(gè)靜態(tài)常量字段,使用內(nèi)部類實(shí)現(xiàn),該內(nèi)部類繼承了枚舉類。所有枚舉常量都通過靜態(tài)代碼塊來(lái)進(jìn)行初始化,即在類加載期間就初始化。
另外通過把clone、readObject、writeObject這三個(gè)方法定義為final的,同時(shí)實(shí)現(xiàn)是拋出相應(yīng)的異常。這樣保證了每個(gè)枚舉類型及枚舉常量都是不可變的??梢岳妹杜e的這兩個(gè)特性來(lái)實(shí)現(xiàn)線程安全的單例。
“Java枚舉類的語(yǔ)法和具體使用方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(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)容。