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

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

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

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

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

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

不能使用 public 來修飾構(gòu)造方法,為什么呢?
這是因為,在 Java 中,枚舉類的構(gòu)造方法都是私有的(不加任何修飾符時,默認(rèn)是 private),無法在枚舉類外部調(diào)用,這也就防止了在枚舉類外部創(chuàng)建新的枚舉常量
在定義枚舉常量時,構(gòu)造方法會被隱式調(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("------------------");
}
}運行結(jié)果:

當(dāng)我們定義帶有參數(shù)的構(gòu)造方法時,在創(chuàng)建枚舉項時,也要為其提供對應(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)缺點
優(yōu)點:
(1)枚舉常量確保了只能使用定義的常量,避免了使用整型常量時可能帶來的錯誤
(2)使用枚舉可以使代碼更易讀,表達(dá)清晰
(3)枚舉定義了一組固定的常量,適合表示有限的狀態(tài)或選項,便于管理和維護(hù)
(4)枚舉可以擁有字段、方法和構(gòu)造方法,能夠封裝與常量相關(guān)的行為和屬性
(5)Java的枚舉類自帶一些方法,如 values()、valueOf() 等
(6)可以用于 switch 語句
缺點:
(1)枚舉的集合是固定的,無法在運行時添加或刪除常量。如果需要動態(tài)的集合,枚舉可能不適用
(2)枚舉不能繼承,無法擴展
(3)每個枚舉常量都是一個對象,可能會增加內(nèi)存使用,特別是當(dāng)枚舉常量數(shù)量較多時
枚舉和反射
在 Java 反射 中,我們學(xué)習(xí)了反射,通過反射,我們可以拿到類的私有構(gòu)造方法,從而創(chuàng)建實例對象
那么,枚舉是否可以通過反射,拿到實例對象呢?
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);
}
}
}運行結(jié)果:

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

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

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

枚舉構(gòu)造方法中不能使用 super
由于枚舉比較特殊,在構(gòu)造方法中,除了我們自定義了兩個參數(shù),它還默認(rèn)添加了父類的兩個參數(shù)
也就是說,構(gòu)造函數(shù)中一共有四個參數(shù):String int String int
其中,前兩個參數(shù)是父類參數(shù),后兩個參數(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);
}
}
}再次運行:

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

