日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

java.lang包—枚举类Enum

發(fā)布時(shí)間:2024/4/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java.lang包—枚举类Enum 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文作者:山高我為

原文地址:java enum的用法詳解

目錄

一、enum關(guān)鍵字

二、Enum類(lèi)源碼

三、疑問(wèn)

四、Enum常見(jiàn)用法


一、enum關(guān)鍵字

enum關(guān)鍵字是在Java1.5也就是Java SE5之后引入的一個(gè)新特性:它通過(guò)關(guān)鍵字enum來(lái)定義一個(gè)枚舉類(lèi),這個(gè)被定義的枚舉類(lèi)繼承Enum類(lèi),這個(gè)枚舉類(lèi)算是一種特殊類(lèi),它同樣能像其他普通類(lèi)一樣擁有構(gòu)造器、方法,也能夠?qū)崿F(xiàn)接口,但是它不能再繼承其他別的類(lèi),因?yàn)樗闹苯痈割?lèi)是Enum類(lèi),并且因?yàn)樗J(rèn)的修飾符有final的存在,因此它無(wú)法直接派生出其他子類(lèi),除非將其使用abstract修飾。

按照《Java編程思想》中的原話來(lái)說(shuō):關(guān)鍵字enum可以將一組具名的值的有限集合創(chuàng)建為一種新的類(lèi)型,而這些具名的值可以作為常規(guī)的程序組件來(lái)使用。

在枚舉類(lèi)出現(xiàn)之前Java是將常量放在接口或是放在普通類(lèi)當(dāng)中,然后使用public、static、final去修飾定義的常量,如下兩個(gè)例子:

public interface Constants2 {public static final int CONSTANT_1 = 1;public static final int CONSTANT_2 = 2;public static final int CONSTANT_3 = 3; }public class Constants {public static final int CONSTANT_1 = 1;public static final int CONSTANT_2 = 2;public static final int CONSTANT_3 = 3; }

在枚舉類(lèi)型出現(xiàn)之后,就可以使用枚舉類(lèi)型來(lái)定義常量,這些枚舉類(lèi)型成員_1、_2、_3都默認(rèn)被public、static、final修飾,語(yǔ)法如下:

public enum Constants {CONSTANT_1,CONSTANT_2,CONSTANT_3 }

?

但是Java枚舉類(lèi)型輸出其常量的時(shí)候不像C?/C++的枚舉那樣是數(shù)字,輸出的是其常量名,如果需要輸出其類(lèi)型成員聲明時(shí)數(shù)字次序的話,需要調(diào)用ordinal()方法:

public enum Singleton2 {SHERLOCK,WASTON; }class Main{public static void main(String[] args) {System.out.println(Singleton2.SHERLOCK);System.out.println(Singleton2.WASTON);System.out.println(Singleton2.SHERLOCK.ordinal());System.out.println(Singleton2.WASTON.ordinal());} }//輸出結(jié)果: //SHERLOCK //WASTON //0 //1

?

二、Enum類(lèi)源碼

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {/*** 枚舉常量的名稱* 使用toString方法訪問(wèn)此字段。*/private final String name;/*** 返回此枚舉常量的名稱,與其枚舉聲明中聲明的完全相同.* 大多數(shù)程序員應(yīng)優(yōu)先使用toString方法,因?yàn)閠oString方法可能會(huì)返回一個(gè)更加友好的名稱。* 此方法主要用于特殊情況,其中正確性取決于獲取確切名稱,不會(huì)因發(fā)行版本而異。*/public final String name() {return name;}/*** 枚舉常量的序數(shù)(它指的是在枚舉聲明中的位置,其中初始常量的序數(shù)為零)。* 大多數(shù)程序員都不會(huì)使用這個(gè)字段。 它設(shè)計(jì)用于復(fù)雜的基于枚舉型的數(shù)據(jù)結(jié)構(gòu),例如EnumSet,EnumMap。*/private final int ordinal;/*** 返回枚舉常量的序數(shù)*/public final int ordinal() {return ordinal;}/*** 唯一的構(gòu)造函數(shù)。 程序員無(wú)法調(diào)用此構(gòu)造函數(shù)。它由編譯器發(fā)出的代碼用于響應(yīng)枚舉類(lèi)型聲明*/protected Enum(String name, int ordinal) {this.name = name;this.ordinal = ordinal;}/*** 返回枚舉常量的名稱。雖然沒(méi)有覆蓋的必要性,但該方法允許進(jìn)行覆蓋。* 當(dāng)存在需要更“友好”的字符串形式時(shí),枚舉類(lèi)型類(lèi)應(yīng)該重寫(xiě)此方法。*/public String toString() {return name;}/*** 如果指定的對(duì)象等于此枚舉常量,則返回true。* 【注意】此處比較形式是通過(guò)“==”進(jìn)行,也即是枚舉類(lèi)之間可以通過(guò) == 進(jìn)行比較*/public final boolean equals(Object other) {return this==other;}/*** 返回此枚舉常量的哈希碼*/public final int hashCode() {return super.hashCode();}/*** 拋出CloneNotSupportedException異常. * 這能保證了枚舉常量類(lèi)永遠(yuǎn)不會(huì)被克隆,從而保證其為”單例”狀態(tài)。*/protected final Object clone() throws CloneNotSupportedException {throw new CloneNotSupportedException();}/*** 將此枚舉與指定的對(duì)象進(jìn)行比較以進(jìn)行排序. * 返回一個(gè)負(fù)整數(shù),零或正整數(shù),因?yàn)榇藢?duì)象小于,等于或大于指定的對(duì)象,枚舉常量只能與其他具有相同枚舉類(lèi)型的枚舉常量相相比較.* 此方法實(shí)現(xiàn)的自然順序是聲明常量的順序*/public final int compareTo(E o) {Enum<?> other = (Enum<?>)o;Enum<E> self = this;if (self.getClass() != other.getClass() && // optimizationself.getDeclaringClass() != other.getDeclaringClass())throw new ClassCastException();return self.ordinal - other.ordinal;}/*** 返回與此枚舉常量的枚舉類(lèi)型對(duì)應(yīng)的Class對(duì)象. * 當(dāng)且僅當(dāng)e1.getDeclaringClass()== e2.getDeclaringClass()時(shí), 兩個(gè)枚舉常量e1和e2屬于相同的枚舉類(lèi)型。*/@SuppressWarnings("unchecked")public final Class<E> getDeclaringClass() {Class<?> clazz = getClass();Class<?> zuper = clazz.getSuperclass();return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;}/*** 返回指定枚舉類(lèi)型的枚舉常量指定的名稱. * 名稱必須與聲明此類(lèi)型的枚舉常量使用的標(biāo)識(shí)符完全匹配(不允許使用無(wú)關(guān)的空格字符.)* 請(qǐng)注意,對(duì)于特定的枚舉類(lèi)型T,可以使用該枚舉上隱式聲明的valueOf(String)方法代替此方法從名稱映射到相應(yīng)的枚舉常量。 * 枚舉類(lèi)型的所有常量都可以通過(guò)調(diào)用該類(lèi)型的隱式方法 values()方法來(lái)獲得。** @param <T> 返回常量的枚舉類(lèi)型* @param 枚舉常量類(lèi)型enumType* @param 要返回的枚舉常量的名稱name* @return 返回具有指定名稱和指定枚舉類(lèi)型的枚舉常量* @throws 如果指定的枚舉類(lèi)型沒(méi)有具有指定名稱的常量,或者指定的類(lèi)對(duì)象不表示枚舉類(lèi)型,拋出IllegalArgumentException 異常* @throws 如果enumType或者code name為null,NullPointerException異常*/public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {T result = enumType.enumConstantDirectory().get(name);if (result != null)return result;if (name == null)throw new NullPointerException("Name is null");throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);}/*** 枚舉類(lèi)不能有finalize方法*/protected final void finalize() { }/*** 無(wú)法反序列化枚舉*/private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {throw new InvalidObjectException("can't deserialize enum");}private void readObjectNoData() throws ObjectStreamException {throw new InvalidObjectException("can't deserialize enum");} }

三、疑問(wèn)

1、為什么說(shuō)enum本質(zhì)是一個(gè)繼承了Enum類(lèi)的class?

Java語(yǔ)法就是這么規(guī)定的,還能為啥

2、枚舉聲明為什么是enum不是class,這樣做的意圖是什么?

3、枚舉允許繼承類(lèi)嗎?可以被別人結(jié)成么?

枚舉不允許繼承類(lèi)。Jvm在生成枚舉時(shí)已經(jīng)繼承了Enum類(lèi),由于Java語(yǔ)言是單繼承,不支持再繼承額外的類(lèi)(唯一的繼承名額被Jvm用了)。也不可以繼承枚舉。因?yàn)镴vm在生成枚舉類(lèi)時(shí),將它聲明為final。

4、枚舉可以用等號(hào)比較嗎?

枚舉可以用等號(hào)比較。Jvm會(huì)為每個(gè)枚舉實(shí)例對(duì)應(yīng)生成一個(gè)類(lèi)對(duì)象,這個(gè)類(lèi)對(duì)象是用public static final修飾的,在static代碼塊中初始化,是一個(gè)單例。

5、為什么使用枚舉代替常量類(lèi)?

在我們平常的開(kāi)發(fā)中,為表示同種類(lèi)型的不同種類(lèi),經(jīng)常的做法是聲明一組具名的int常量來(lái)表示,每個(gè)類(lèi)型成員一個(gè)常量,如:

public static final int DAY_MONDAY = 1; public static final int DAY_TUESDAY = 2; public static final int DAY_WEDNESDAY = 3; public static final int DAY_THURSDAY = 4; public static final int DAY_FRIDAY = 5; public static final int DAY_SATURDAY = 6; public static final int DAY_SUNDAY = 7;public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2;

這種方法稱做?int枚舉模式,這種方式在安全性和使用方便性方面沒(méi)有任何幫助。

a、將day傳到想要orange的方法中,編譯器不會(huì)警告,執(zhí)行也不會(huì)出現(xiàn)錯(cuò)誤;
b、用==操作符將day與orange比較,編譯器不會(huì)警告,執(zhí)行也不會(huì)出現(xiàn)錯(cuò)誤;
c、int枚舉是編譯時(shí)常量,被編譯到客戶端中,如果枚舉常量關(guān)聯(lián)的int發(fā)生變化,客戶端必須重新編譯,如果沒(méi)有重新編譯,程序仍可以運(yùn)行,但行為就確定了,如DAY_MONDAY關(guān)聯(lián)的常量不再是1,而是0。
d、將int枚舉常量翻譯成可打印的字符串很麻煩
e、如果想要遍歷一個(gè)組中的所有int 枚舉常量,甚至獲得int枚舉組的大小,這種實(shí)現(xiàn)沒(méi)有啥方便可靠的方法。

因此,推薦使用枚舉類(lèi)型來(lái)代替這種int枚舉常量:

public enum DAY {DAY_MONDAY, DAY_TUESDAY, DAY_WEDNESDAY,DAY_THURSDAY, DAY_FRIDAY, DAY_SATURDAY, DAY_SUNDAY}public enum ORANGE {ORANGE_NAVEL, ORANGE_TEMPLE, ORANGE_BLOOD}

這種枚舉類(lèi)型,提供了編譯時(shí)的類(lèi)型安全檢查,如果聲明了一個(gè)參數(shù)的類(lèi)型為DAY,就可以保證,被傳到該參數(shù)上的任何非null的對(duì)象引用一定屬于其他有效值中的一個(gè),試圖傳遞類(lèi)型錯(cuò)誤的值時(shí),會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤,就像試圖將某種枚舉類(lèi)型的表達(dá)式賦給另一種枚舉類(lèi)型的變量,或者試圖利用==操作符比較不同枚舉類(lèi)型的值一樣。同時(shí)包含同名常量的多個(gè)枚舉類(lèi)型可以共存,因?yàn)槊總€(gè)類(lèi)型有自己的命名空間,增加或重新排列枚舉類(lèi)型的常量,無(wú)需重新編譯客戶端的代碼。如果想獲取類(lèi)型對(duì)應(yīng)的字符串,直接通過(guò)toString方法即可。

枚舉類(lèi)型除了完善了int枚舉模式的不足之處外,枚舉類(lèi)型還允許添加任意的方法和域,并實(shí)現(xiàn)任意的接口。這個(gè)有什么用途呢?

a、能夠?qū)?shù)據(jù)與它的常量關(guān)聯(lián)起來(lái),例如能夠返回水果顏色或者水果圖片的方法,對(duì)于我們的ORANGE類(lèi)型來(lái)說(shuō)可能就很有好處;
b、你可使用適當(dāng)?shù)姆椒▉?lái)增強(qiáng)枚舉類(lèi)型,枚舉類(lèi)型可以先作為枚舉常量的一個(gè)簡(jiǎn)單集合,隨著時(shí)間的推移在演變成為全功能的抽象。

另外,當(dāng)你想要每增加一種枚舉常量時(shí),需要強(qiáng)制選擇一種對(duì)應(yīng)的策略,可以使用枚舉提供的策略枚舉(strategy enum)?的方式

4、究竟是枚舉的性能好,還是常量類(lèi)好?

5、為什么枚舉要實(shí)現(xiàn)Comparable接口?

6、為什么枚舉要實(shí)現(xiàn)Serializable接口?

7、為什么枚舉支持泛型?

8、枚舉的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組還是鏈表?

9、為什么枚舉類(lèi)型實(shí)例化就能訪?比如如下代碼為什么不報(bào)錯(cuò)

public class Traffic{public enum Light{GREEN,YELLOW,RED} } Traffic.Light state = Traffic.Light.GREEN;

Java枚舉類(lèi)型都是靜態(tài)常量,隱式的用static final修飾過(guò)。確切的說(shuō),Java枚舉類(lèi)型是“靜態(tài)常量”,這里面包含了兩層意思:

  • 枚舉型中的實(shí)例隱式地用static final修飾過(guò)。
  • 枚舉型作為某個(gè)類(lèi)中的成員字段也隱式的用static final修飾過(guò)

還是你上面這個(gè)代碼,反編譯一下,你就能看到--編譯器背著你偷偷做了哪些手腳

  • 首先,枚舉型Light是個(gè)實(shí)實(shí)在在的類(lèi)。集成自基類(lèi)Enum<Light>。然后在你不知情的情況下,偷偷加了static final修飾詞。然后3個(gè)枚舉實(shí)例?GREEN,YELLOW,RED也確確實(shí)實(shí)是light的實(shí)例,然后前面加了static final。
  • 然后構(gòu)造器也被偷偷的閹割成private。這種實(shí)例控制手段,是不是在單例模式里面見(jiàn)過(guò),所以枚舉也是實(shí)現(xiàn)單例器的一種方法。
  • 然后編譯器還偷偷的告訴Light[]數(shù)組,一個(gè)values()方法,一個(gè)valueO()f方法,這個(gè)values在Enum文檔里面找不到,如果在Enum里面定義一個(gè)相關(guān)方法,你還會(huì)看到一個(gè)匿名內(nèi)部類(lèi)

反編譯的結(jié)果如下:

總之,Java的Enum枚舉類(lèi)型就是一個(gè)大大的“語(yǔ)法糖”。明明是一個(gè)完整的類(lèi),但只向用戶暴露幾個(gè)常態(tài)變量,隱藏掉大部分實(shí)現(xiàn)細(xì)節(jié)。

上述文字引用自知乎問(wèn)答:Java 枚舉型為什么是靜態(tài)的,以及是怎么實(shí)現(xiàn)的?胖君的回答

10、是不是所有的枚舉都默認(rèn)是靜態(tài)的?

通過(guò)可問(wèn)題5,可知所有的枚舉都默認(rèn)是靜態(tài)的

?

11、枚舉有哪些應(yīng)用場(chǎng)景?

12、枚舉是如何實(shí)現(xiàn)單例的?

public enum Singleton2 {SHERLOCK }class Main{public static void main(String[] args) {Singleton2 sherlock = Singleton2.SHERLOCK;Singleton2 sherlock1 = Singleton2.SHERLOCK;System.out.println(sherlock == Singleton2.SHERLOCK);System.out.println(sherlock == sherlock1);System.out.println(Singleton2.SHERLOCK.getDeclaringClass());} }輸出結(jié)果: true true class com.sherlock.singleton.Singleton2

四、Enum常見(jiàn)用法

用法一:常量

在JDK1.5 之前,我們定義常量都是: public static fianl.... 。現(xiàn)在好了,有了枚舉,可以把相關(guān)的常量分組到一個(gè)枚舉類(lèi)型里,而且枚舉提供了比常量更多的方法。

public enum Color { RED, GREEN, BLANK, YELLOW }

用法二:switch

JDK1.6之前的switch語(yǔ)句只支持int,char,enum類(lèi)型,使用枚舉,能讓我們的代碼可讀性更強(qiáng)。

enum Signal {GREEN, YELLOW, RED}public class TrafficLight {Signal color = Signal.RED;public void change() {switch (color) {case RED:color = Signal.GREEN;break;case YELLOW:color = Signal.RED;break;case GREEN:color = Signal.YELLOW;break;}} }

?

用法三:向枚舉中添加新方法

如果打算自定義自己的方法,那么必須在enum實(shí)例序列的最后添加一個(gè)分號(hào)。而且 Java 要求必須先定義 enum 實(shí)例。

?

public enum Color {RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);// 成員變量private String name;private int index;// 構(gòu)造方法private Color(String name, int index) {this.name = name;this.index = index;}// 普通方法public static String getName(int index) {for (Color c : Color.values()) {if (c.getIndex() == index) {return c.name;}}return null;}// get set 方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;} }

?

用法四:覆蓋枚舉的方法

下面給出一個(gè)toString()方法覆蓋的例子。

public class Test {public enum Color {RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);// 成員變量private String name;private int index;// 構(gòu)造方法private Color(String name, int index) {this.name = name;this.index = index;}// 覆蓋方法@Overridepublic String toString() {return this.index + "_" + this.name;}}public static void main(String[] args) {System.out.println(Color.RED.toString());} }

用法五:實(shí)現(xiàn)接口

所有的枚舉都繼承自java.lang.Enum類(lèi)。由于Java 不支持多繼承,所以枚舉對(duì)象不能再繼承其他類(lèi)。

?

public interface Behaviour {void print();String getInfo(); }public enum Color implements Behaviour {RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);// 成員變量private String name;private int index;// 構(gòu)造方法private Color(String name, int index) {this.name = name;this.index = index;}// 接口方法@Overridepublic String getInfo() {return this.name;}// 接口方法@Overridepublic void print() {System.out.println(this.index + ":" + this.name);} }

?

用法六:使用接口組織枚舉?

?

public interface Food {enum Coffee implements Food {BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO}enum Dessert implements Food {FRUIT, CAKE, GELATO} }

?

用法七:關(guān)于枚舉集合的使用

java.util.EnumSet和java.util.EnumMap是兩個(gè)枚舉集合。EnumSet保證集合中的元素不重復(fù);EnumMap中的 key是enum類(lèi)型,而value則可以是任意類(lèi)型。關(guān)于這個(gè)兩個(gè)集合的使用就不在這里贅述,可以參考JDK文檔。?完整示例代碼

枚舉類(lèi)型的完整演示代碼如下:

?

public class LightTest {// 1.定義枚舉類(lèi)型public enum Light {// 利用構(gòu)造函數(shù)傳參RED(1), GREEN(3), YELLOW(2);// 定義私有變量private int nCode;// 構(gòu)造函數(shù),枚舉類(lèi)型只能為私有private Light(int _nCode) {this.nCode = _nCode;}@Overridepublic String toString() {return String.valueOf(this.nCode);}}public static void main(String[] args) {// 1.遍歷枚舉類(lèi)型System.out.println("演示枚舉類(lèi)型的遍歷 ......");testTraversalEnum();// 2.演示EnumMap對(duì)象的使用System.out.println("演示EnmuMap對(duì)象的使用和遍歷.....");testEnumMap();// 3.演示EnmuSet的使用System.out.println("演示EnmuSet對(duì)象的使用和遍歷.....");testEnumSet();}/*** * 演示枚舉類(lèi)型的遍歷*/private static void testTraversalEnum() {Light[] allLight = Light.values();for (Light aLight : allLight) {System.out.println("當(dāng)前燈name:" + aLight.name());System.out.println("當(dāng)前燈ordinal:" + aLight.ordinal());System.out.println("當(dāng)前燈:" + aLight);}}/*** * 演示EnumMap的使用,EnumMap跟HashMap的使用差不多,只不過(guò)key要是枚舉類(lèi)型*/private static void testEnumMap() {// 1.演示定義EnumMap對(duì)象,EnumMap對(duì)象的構(gòu)造函數(shù)需要參數(shù)傳入,默認(rèn)是key的類(lèi)的類(lèi)型EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>(Light.class);currEnumMap.put(Light.RED, "紅燈");currEnumMap.put(Light.GREEN, "綠燈");currEnumMap.put(Light.YELLOW, "黃燈");// 2.遍歷對(duì)象for (Light aLight : Light.values()) {System.out.println("[key=" + aLight.name() + ",value="+ currEnumMap.get(aLight) + "]");}}/*** * 演示EnumSet如何使用,EnumSet是一個(gè)抽象類(lèi),獲取一個(gè)類(lèi)型的枚舉類(lèi)型內(nèi)容<BR/>* * 可以使用allOf方法*/private static void testEnumSet() {EnumSet<Light> currEnumSet = EnumSet.allOf(Light.class);for (Light aLightSetElement : currEnumSet) {System.out.println("當(dāng)前EnumSet中數(shù)據(jù)為:" + aLightSetElement);}} }

?

執(zhí)行結(jié)果如下:

演示枚舉類(lèi)型的遍歷 ...... 當(dāng)前燈name:RED 當(dāng)前燈ordinal:0 當(dāng)前燈:1 當(dāng)前燈name:GREEN 當(dāng)前燈ordinal:1 當(dāng)前燈:3 當(dāng)前燈name:YELLOW 當(dāng)前燈ordinal:2 當(dāng)前燈:2 演示EnmuMap對(duì)象的使用和遍歷..... [key=RED,value=紅燈] [key=GREEN,value=綠燈] [key=YELLOW,value=黃燈] 演示EnmuSet對(duì)象的使用和遍歷..... 當(dāng)前EnumSet中數(shù)據(jù)為:1 當(dāng)前EnumSet中數(shù)據(jù)為:3 當(dāng)前EnumSet中數(shù)據(jù)為:2

讀后有收獲可以支付寶請(qǐng)作者喝奶茶?

?

總結(jié)

以上是生活随笔為你收集整理的java.lang包—枚举类Enum的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。