Java?枚舉的定義、使用詳解
枚舉是什么
枚舉(enum):是一種特殊的類,用于定義一組常量,將其組織起來(lái)。枚舉使得代碼更具有可讀性和可維護(hù)性,特別是在處理固定集合的值時(shí),如:星期、月份、狀態(tài)碼等
在 Java 中,使用關(guān)鍵字 enum 來(lái)定義枚舉類:
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;
}
其中,定義的枚舉項(xiàng)就是該類的實(shí)例,且必須在第一行,最后一個(gè)枚舉項(xiàng)后的分號(hào); 可以省略,但是若枚舉類有其他內(nèi)容,則分號(hào)不能省略(最好不要省略)
當(dāng)類初始化時(shí),這些枚舉項(xiàng)就會(huì)被實(shí)例化
枚舉類使用 enum 定義后,默認(rèn)繼承 java.lang.Enum 類,也就是說(shuō),我們自己寫的枚舉類,就算沒(méi)有顯示的繼承 Enum,但是其默認(rèn)繼承了這個(gè)類
此外,枚舉在 Java 中不能被繼承,自定義的枚舉類隱式繼承自 java.lang.Enum 類,且不能再繼承其他類,這樣的設(shè)計(jì)確保了枚舉類的簡(jiǎn)單性和一致性。如果枚舉可以繼承其他類,將會(huì)導(dǎo)致復(fù)雜的繼承關(guān)系,并且影響Java的類型系統(tǒng)
常用方法
| 方法 | 描述 |
|---|---|
| values() | 以數(shù)組的形式返回枚舉類型的所有成員 |
ordinal() | 獲取枚舉成員的索引位置 |
| valueOf() | 將普通字符串轉(zhuǎn)換為枚舉實(shí)例 |
compareTo(E o) | 比較兩個(gè)枚舉成員在定義時(shí)的順序 |
我們通過(guò)一個(gè)示例,來(lái)學(xué)習(xí)和使用這些方法:
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;
public static void main(String[] args) {
// 獲取所有枚舉成員
Day[] days = Day.values();
// 遍歷
for (int i = 0; i < days.length; i++) {
// 獲取枚舉成員以及索引位置
System.out.println(days[i] + " " + days[i].ordinal());
}
// 將普通字符串轉(zhuǎn)換為枚舉實(shí)例
System.out.println(Day.valueOf("THURSDAY"));
// 獲取枚舉實(shí)例 SUNDAY 和 SATURDAY
Day sunday = Day.SUNDAY;
Day saturday = Day.SATURDAY;
// 比較定義時(shí)的順序
System.out.println(sunday.compareTo(saturday));
}
}運(yùn)行結(jié)果:

在使用 valueOf() 方法進(jìn)行轉(zhuǎn)換時(shí),傳遞的名稱必須與枚舉常量的名字完全匹配(包括大小寫),若不匹配,就會(huì)拋出 IllegalArgumentException 異常:

當(dāng)我們查看 java.lang.Enum 時(shí):

可以看到,valueOf() 方法包含了兩個(gè)類型的參數(shù):Class<T> enumClass 和 String name
其中
Class<T> enumType 是一個(gè) Class 對(duì)象,表示要查找的枚舉類型
String name: 是一個(gè)字符串,表示要查找的枚舉常量的名稱。名稱必須與枚舉常量的名字完全匹配(包括大小寫)
但是在使用時(shí),我們只傳遞了一個(gè)參數(shù) name,也能夠進(jìn)行轉(zhuǎn)換,這是為什么呢?
這是因?yàn)?,?Java 中,valueOf 方法實(shí)際上是自動(dòng)生成的,屬于每個(gè)枚舉類型的特性。雖然它的原始定義需要兩個(gè)參數(shù)(類類型和名稱),但是每個(gè)枚舉類型都會(huì)自動(dòng)提供一個(gè)與自身類型相關(guān)聯(lián)的 valueOf 方法,只需傳遞一個(gè)字符串參數(shù)
因此,當(dāng)我們調(diào)用 Day.valueOf("THURSDAY") 時(shí),Java會(huì)自動(dòng)處理這個(gè)調(diào)用,實(shí)際調(diào)用的是包含類名的 valueOf 方法,而不是原始的靜態(tài)方法定義
再觀察 java.lang.Enum:

我們會(huì)發(fā)現(xiàn),其中并不存在 values() 這個(gè)方法,而當(dāng)我們點(diǎn)擊 values() 方法時(shí),則會(huì)跳轉(zhuǎn)到本類上
那么,values() 方法是從哪來(lái)的呢?
values() 方法是枚舉類自動(dòng)提供的一個(gè)靜態(tài)方法,允許我們獲取一個(gè)包含所有枚舉常量的數(shù)組,這個(gè)方法是由Java編譯器自動(dòng)生成的,在編譯時(shí)每個(gè)枚舉類型都會(huì)自動(dòng)生成一個(gè) values() 方法,因此不需要我們顯式定義它
我們將枚舉類進(jìn)行反編譯:
(1)打開 cmd,切換到 Day.java 文件所在目錄
(2)編譯 .java 文件(javac Day.java)
(3)將 .class 文件進(jìn)行反編譯(javap -c Day.class > day.txt)
打開 day.txt,可以看到:

編譯器自動(dòng)為我們生成了 values 和 valueOf 方法
構(gòu)造方法
當(dāng)我們創(chuàng)建構(gòu)造方法時(shí):

不能使用 public 來(lái)修飾構(gòu)造方法,為什么呢?
這是因?yàn)?,?Java 中,枚舉類的構(gòu)造方法都是私有的(不加任何修飾符時(shí),默認(rèn)是 private),無(wú)法在枚舉類外部調(diào)用,這也就防止了在枚舉類外部創(chuàng)建新的枚舉常量
在定義枚舉常量時(shí),構(gòu)造方法會(huì)被隱式調(diào)用:
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;
Day() {
System.out.println("構(gòu)造方法");
}
public static void main(String[] args) {
System.out.println("------------------");
}
}運(yùn)行結(jié)果:

當(dāng)我們定義帶有參數(shù)的構(gòu)造方法時(shí),在創(chuàng)建枚舉項(xiàng)時(shí),也要為其提供對(duì)應(yīng)參數(shù):
public enum Day {
SUNDAY("周天", 7),
MONDAY("周一", 1),
TUESDAY("周二", 2),
WEDNESDAY("周三", 3),
THURSDAY("周四", 4),
FRIDAY("周五", 5),
SATURDAY("周六", 6);
private String name;
private int key;
Day(String name, int key) {
this.name = name;
this.key = key;
}
}枚舉的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
(1)枚舉常量確保了只能使用定義的常量,避免了使用整型常量時(shí)可能帶來(lái)的錯(cuò)誤
(2)使用枚舉可以使代碼更易讀,表達(dá)清晰
(3)枚舉定義了一組固定的常量,適合表示有限的狀態(tài)或選項(xiàng),便于管理和維護(hù)
(4)枚舉可以擁有字段、方法和構(gòu)造方法,能夠封裝與常量相關(guān)的行為和屬性
(5)Java的枚舉類自帶一些方法,如 values()、valueOf() 等
(6)可以用于 switch 語(yǔ)句
缺點(diǎn):
(1)枚舉的集合是固定的,無(wú)法在運(yùn)行時(shí)添加或刪除常量。如果需要?jiǎng)討B(tài)的集合,枚舉可能不適用
(2)枚舉不能繼承,無(wú)法擴(kuò)展
(3)每個(gè)枚舉常量都是一個(gè)對(duì)象,可能會(huì)增加內(nèi)存使用,特別是當(dāng)枚舉常量數(shù)量較多時(shí)
枚舉和反射
在 Java 反射 中,我們學(xué)習(xí)了反射,通過(guò)反射,我們可以拿到類的私有構(gòu)造方法,從而創(chuàng)建實(shí)例對(duì)象
那么,枚舉是否可以通過(guò)反射,拿到實(shí)例對(duì)象呢?
public enum Day {
SUNDAY("周天", 7),
MONDAY("周一", 1),
TUESDAY("周二", 2),
WEDNESDAY("周三", 3),
THURSDAY("周四", 4),
FRIDAY("周五", 5),
SATURDAY("周六", 6);
private String name;
private int key;
Day(String name, int key) {
this.name = name;
this.key = key;
}
}public class Test {
public static void main(String[] args) {
Class<?> classDay = null;
try {
classDay = Class.forName("Day");
Constructor<?> constructor = classDay.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Day day = (Day) constructor.newInstance("sunday", 0);
System.out.println(day.ordinal());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}運(yùn)行結(jié)果:

此時(shí)程序拋出了 NoSuchMethodException 異常,也就是沒(méi)有對(duì)應(yīng)的構(gòu)造方法
但是提供的枚舉的構(gòu)造方法就是帶有兩個(gè)參數(shù),分別為 String 和 int:

那么,問(wèn)題出在哪里呢?
自定義的枚舉類默認(rèn)繼承自 java.lang.Enum
因此,自定義的枚舉類繼承了父類除構(gòu)造方法外的所有東西,且子類需要幫助父類進(jìn)行構(gòu)造,
但我們實(shí)現(xiàn)的類中,并沒(méi)有幫助父類進(jìn)行構(gòu)造
因此,我們需要在枚舉類中幫助父類進(jìn)行構(gòu)造,而父類中的構(gòu)造方法為:

那么,如何實(shí)現(xiàn)呢?通過(guò) super 方法嗎?
但是,當(dāng)我們?cè)跇?gòu)造方法中調(diào)用 super 時(shí):

枚舉構(gòu)造方法中不能使用 super
由于枚舉比較特殊,在構(gòu)造方法中,除了我們自定義了兩個(gè)參數(shù),它還默認(rèn)添加了父類的兩個(gè)參數(shù)
也就是說(shuō),構(gòu)造函數(shù)中一共有四個(gè)參數(shù):String int String int
其中,前兩個(gè)參數(shù)是父類參數(shù),后兩個(gè)參數(shù)是子類參數(shù)
public class Test {
public static void main(String[] args) {
Class<?> classDay = null;
try {
classDay = Class.forName("enumDemo.Day");
Constructor<?> constructor = classDay.getDeclaredConstructor(String.class, int.class, String.class, int.class);
constructor.setAccessible(true);
Day day = (Day) constructor.newInstance("父類參數(shù)", 0, "子類參數(shù)", 0);
System.out.println(day.ordinal());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}再次運(yùn)行:

此時(shí)拋出了 IllegalArgumentException 異常,不能通過(guò)反射創(chuàng)建枚舉對(duì)象
枚舉保證了每個(gè)枚舉常量只有一個(gè)實(shí)例,這種唯一性在枚舉類型被定義時(shí)就已經(jīng)確定,不允許外部創(chuàng)建新的實(shí)例,枚舉類型的設(shè)計(jì)使得它們的實(shí)例在類加載時(shí)被唯一地定義,從而避免了通過(guò)反射創(chuàng)建新的枚舉實(shí)例的可能性,確保了枚舉的強(qiáng)類型安全性和唯一性
實(shí)現(xiàn)單例模式
在 單例模式:餓漢模式、懶漢模式_單例模式懶漢和餓漢-CSDN博客 中我們實(shí)現(xiàn)了單例模式,單例模式能夠確保一個(gè)類只有一個(gè)實(shí)例
但普通類可以通過(guò)反射機(jī)制打破,因此,我們可以使用枚舉來(lái)實(shí)現(xiàn)單例模式
public enum Singleton {
INSTANCE;
public Singleton getInstance() {
return INSTANCE;
}
}到此這篇關(guān)于Java 枚舉的定義、使用詳解的文章就介紹到這了,更多相關(guān)Java 枚舉內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 數(shù)據(jù)結(jié)構(gòu)并查集詳解
并查集是一種用來(lái)管理元素分組情況的數(shù)據(jù)結(jié)構(gòu)。并查集可以高效地進(jìn)行如下操作。本文將通過(guò)Java實(shí)現(xiàn)并查集,感興趣的小伙伴可以了解一下2022-03-03
openGauss數(shù)據(jù)庫(kù)JDBC環(huán)境連接配置的詳細(xì)過(guò)程(Eclipse)
這篇文章主要介紹了openGauss數(shù)據(jù)庫(kù)JDBC環(huán)境連接配置(Eclipse),演示基于JDBC開發(fā)的主要步驟,會(huì)涉及創(chuàng)建數(shù)據(jù)庫(kù)、創(chuàng)建表、插入數(shù)據(jù)等,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
Java 異常java.lang.NoSuchFieldException解決方案
這篇文章主要介紹了Java 異常java.lang.NoSuchFieldException解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法
本文主要介紹了mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
SpringBoot 關(guān)于Feign的超時(shí)時(shí)間配置操作
這篇文章主要介紹了SpringBoot 關(guān)于Feign的超時(shí)時(shí)間配置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
超細(xì)講解Java調(diào)用python文件的幾種方式
有時(shí)候我們?cè)趯慾ava的時(shí)候需要調(diào)用python文件,下面這篇文章主要給大家介紹了關(guān)于Java調(diào)用python文件的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
解決idea反編譯失敗無(wú)法查看jar包的源碼問(wèn)題
在IntelliJ IDEA中查看jar包源碼的兩種方法:下載反編譯軟件JD-GUI或安裝JavaBytecodeDecompiler插件2024-12-12
Java簡(jiǎn)單實(shí)現(xiàn)UDP和TCP的示例
下面小編就為大家?guī)?lái)一篇Java簡(jiǎn)單實(shí)現(xiàn)UDP和TCP的示例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Java 內(nèi)省introspector相關(guān)原理代碼解析
這篇文章主要介紹了Java 內(nèi)省introspector相關(guān)原理代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

