Java枚舉解讀
Java枚舉
枚舉類概念的理解與定義
- 一個類的對象是有限個,確定的,我們稱此為枚舉類。
- 當(dāng)需要定義和維護(hù)一組常量時,強(qiáng)烈建議使用枚舉類。
- 如果一個枚舉類中只有一個對象,則可以作為單例模式的實(shí)現(xiàn)方式。
通俗的說:一個類被設(shè)計(jì)為包含固定實(shí)例數(shù)量的特殊類,我們給他的定義是枚舉類。
注意:
1.枚舉類不能被 new 出來,枚舉類因?yàn)槟J(rèn)的類修飾符為 final 所以也不能被派生(繼承),同理枚舉類也不能為當(dāng)作實(shí)現(xiàn)。
2.枚舉類自身可以實(shí)現(xiàn)接口,既可以進(jìn)行統(tǒng)一實(shí)現(xiàn)重寫接口抽象方法,也可以按照枚舉類型單個實(shí)現(xiàn)重寫。
枚舉類的定義
關(guān)于枚舉類的定義,這塊主要想和大家分享兩種方式
- jdk 5.0之前,自定義枚舉類方式
- jdk 5.0之后,Enum關(guān)鍵字方式定義
實(shí)踐
一、準(zhǔn)備工作
我們新建一個 Java Project ,并創(chuàng)建一個包,以及一個測試類

二、自定義枚舉的三種方式(jdk 5.0 之前)
1. 定義一個抽象類,在抽象類中定義常量進(jìn)行維護(hù),我們接下來以 Java 類庫中的 Calendar 類示例來進(jìn)行說明
新建一個類 EnumDemo01.java 代碼如下:
package org.taoguoguo;
import java.util.Calendar;
/**
* @author taoGG
* @description jdk 5.0 之前 抽象類枚舉方案Demo
* @create 2020-09-13 14:20
*/
public class EnumDemo01 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(1));
}
}
Console 結(jié)果輸出:
2020
Process finished with exit code 0
如果熟悉 Calendar API 的小伙伴 應(yīng)該馬上能反應(yīng)過來,這個是獲取當(dāng)前的年份,類似的值還有
3 - 一年中的第幾個星期
4 - 一年中的第幾個月
5 - 當(dāng)前的日期
......
但是這么多值,我們怎么能記得住呢?萬一我輸入錯誤,隨便取了一個范圍怎么辦?
沒錯,這是 jdk 5.0之前的痛點(diǎn),為了解決實(shí)例數(shù)量固定,便于維護(hù)這些問題,在jdk 5.0之后更新Enum枚舉類解決了這個問題。那在jdk 5.0之前官方是怎么做的呢?難道需要我們一個個去記住 Calendar 的數(shù)字?
實(shí)際上官方本身,采用的就是我們現(xiàn)在說的第一種方式,在抽象類中定義常量進(jìn)行維護(hù)
現(xiàn)在我們將代碼做些修改:
package org.taoguoguo;
import java.util.Calendar;
/**
* @author taoGG
* @description jdk 5.0 之前 抽象類枚舉方案Demo
* @create 2020-09-13 14:20
*/
public class EnumDemo01 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.YEAR));
}
}
我們運(yùn)行進(jìn)行輸出:
2020
Process finished with exit code 0
結(jié)果與之前一致,這時我們就清楚,在開發(fā)過程中作為開發(fā)者我們肯定愿意使用 Calendar.YEAR 這種寫法,一來方便記憶,二來可讀性高。那么官方的做法時怎樣的呢?我們點(diǎn)進(jìn)去源碼看一下
-
首先 Calendar 本身是一個抽象類,實(shí)現(xiàn)了序列化、克隆、以及比較排序接口,這邊和我們枚舉沒有太大關(guān)系,我們繼續(xù)往下看

-
在抽象類中,定義了很多個靜態(tài)常量進(jìn)行維護(hù),而當(dāng)我們需要使用時,直接調(diào)用,這樣就比我們寫一個個的具體值要方便和易用了。

2. 定義一個接口,在接口中定義常量維護(hù)枚舉值
我們新建一個interface CustomerInf.java
package org.taoguoguo;
/**
* @author taoGG
* @description 接口常量維護(hù)枚舉值
* @create 2020-09-13 15:47
*/
public interface CustomerInf {
int RED = 1;
int GREEN = 2;
int BLUE = 3;
}
在 EnumTest 進(jìn)行測試
package org.taoguoguo;
/**
* @author taoGG
* @description Java枚舉測試類
* @create 2020-09-13 14:54
*
*/
public class EnumTest {
public static void main(String[] args) {
System.out.println(CustomerInf.RED);
}
}
測試結(jié)果:
1
Process finished with exit code 0
這種做法我們達(dá)到了和在抽象類中維護(hù)常量相同的目的。上面這兩種做法都非常的簡單易用,但也有弊端。比如我們只知道一個狀態(tài)值,當(dāng)我們要獲取狀態(tài)的屬性或者相關(guān)的內(nèi)容時,我們該怎么做呢?
下面我們使用第三種方式,自定義枚舉類,這種基本上達(dá)到和 Enum 關(guān)鍵字相同的作用,但有一點(diǎn)不足就是會較為復(fù)雜
3.自定義枚舉類,通過為類私有化構(gòu)造器和固定實(shí)例對象進(jìn)行枚舉維護(hù)
新建一個class SeasonEnum.java,代碼如下:
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 15:58
*/
public class SeasonEnum {
//1.聲明枚舉對象的屬性
private final String seasonName;
private final int code;
//2.私有化類的構(gòu)造器
private SeasonEnum(String seasonName,int code){
this.seasonName = seasonName;
this.code = code;
}
//3.提供當(dāng)前枚舉類的多個對象 public static final
public static final SeasonEnum SPRING = new SeasonEnum("春天",100);
public static final SeasonEnum SUMMER = new SeasonEnum("夏天",200);
public static final SeasonEnum AUTUMN = new SeasonEnum("秋天",300);
public static final SeasonEnum WINTER = new SeasonEnum("冬天",400);
//4.為類提供獲取屬性的方法
public String getSeasonName() {
return seasonName;
}
public int getCode() {
return code;
}
//5.重寫toString方法
@Override
public String toString() {
return "SeasonEnum{" +
"seasonName='" + seasonName + '\'' +
", code=" + code +
'}';
}
}
新建一個class SeasonEnumTest 進(jìn)行測試,當(dāng)我們通過自定義枚舉類引用實(shí)例對象時,如下圖可以看到,我們已經(jīng)可以獲取到我們的枚舉對象了。

獲取到枚舉對象,我們當(dāng)然也可以獲取到對應(yīng)的屬性及方法,這種可用性就提高了很多,我們在開發(fā)程序進(jìn)行判斷,可以根據(jù)各種枚舉值的指定屬性來進(jìn)行,提高了代碼的可維護(hù)性。

SeasonEnumTest 測試代碼
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:04
*/
public class SeasonEnumTest {
public static void main(String[] args) {
SeasonEnum spring = SeasonEnum.SPRING;
System.out.println("自定義枚舉類對象:" + spring);
System.out.println("自定義枚舉類屬性:" + spring.getSeasonName());
System.out.println("自定義枚舉類屬性:" + spring.getCode());
}
}
根據(jù)我們上面的自定義枚舉類方式,我們基本已經(jīng)實(shí)現(xiàn)了枚舉的功能了,但是就像上面說到的,如果開發(fā)中枚舉類型較多,開發(fā)多個這樣的自定義枚舉類會非常的耗時,所以 jdk 5.0 之后,推出了 Enum 關(guān)鍵字定義枚舉類
三、Enum 關(guān)鍵字定義枚舉類(jdk 5.0之后)
enum 全稱為 enumeration,是jdk 5.0 中引入的新特性,在Java 中被 enum 關(guān)鍵字修飾的類型就是枚舉類型
我們通過代碼來示例來講解和理解 enum 的用法,還是用我們剛剛自定以枚舉類的例子,看看使用enum如何來寫
新建一個Java class ,Kind 類型選擇 enum 如圖:

枚舉類創(chuàng)建注意:
- 枚舉實(shí)例必須在
enum關(guān)鍵字聲明的類中顯式的指定(首行開始的以第一個分號結(jié)束) - 枚舉不允許使用new,clone,反射,序列化手動創(chuàng)建枚舉實(shí)例
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:23
*/
public enum Season {
SPRING("春天",100),
SUMMER("夏天",200),
AUTUMN("秋天",300),
WINTER("冬天",400);
private final String seasonName;
private final int code;
Season(String seasonName, int code){
this.seasonName = seasonName;
this.code = code;
}
public String getSeasonName() {
return seasonName;
}
public int getCode() {
return code;
}
}
使用 SeasonTest 測試類進(jìn)行測試:
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:27
*/
public class SeasonTest {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
}
輸出結(jié)果:
SPRING
Process finished with exit code 0
注意,在enmu 枚舉類中如果沒有重寫 toString方法,會默認(rèn)使用Enum類本身提供的 toString 方法,返回枚舉類名稱,因?yàn)槎x的枚舉類默認(rèn)隱式繼承于java.lang.Enum
1.枚舉類主要方法介紹
values():該方法可以返回當(dāng)前枚舉類型的對象數(shù)組,可以很方便的遍歷所有枚舉值。一般我們可以根據(jù)枚舉類的相關(guān)屬性通過此方法遍歷獲取對應(yīng)的枚舉對象及枚舉值valueOf(String str): 根據(jù)枚舉類名稱獲取枚舉類對象toString(): 默認(rèn)使用 java.lang.Enum的 toString方法,返回當(dāng)前對象常量的名稱,枚舉類推薦重寫返回自定義友好描述name(): 返回當(dāng)前枚舉對象名稱,和toString作用上類似,當(dāng)時toString支持重寫,name方法是不能重寫的,在本質(zhì)上 toString 也是調(diào)用的 name方法,枚舉定義 name 方法就是為了返回枚舉對象名稱,而 toString 應(yīng)該根據(jù)需要進(jìn)行重寫ordinal(): 返回當(dāng)前枚舉對象的序號, 實(shí)現(xiàn)了 Comparable 接口,表明它是支持排序的 可以通過Collections.sort進(jìn)行自動排序比較此枚舉與指定對象的順序compareTo(): 基于ordinal進(jìn)行序號大小比較
方式演示代碼,小伙伴們可以自行運(yùn)行輸出一下,看看各個方法的作用,熟悉一下相關(guān)的方法api
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:27
*/
public class SeasonTest {
public static void main(String[] args) {
System.out.println("========values()方法=======");
for (Season season : Season.values()) {
System.out.println(season);
}
System.out.println("===========================");
System.out.println("========valueOf方法========");
Season spring = Season.valueOf("SPRING");
System.out.println(spring);
System.out.println("===========================");
System.out.println("========toString方法========");
System.out.println(spring.toString());
System.out.println("===========================");
System.out.println("========name方法========");
System.out.println(spring.name());
System.out.println("===========================");
System.out.println("========ordinal方法========");
System.out.println(spring.ordinal());
System.out.println("===========================");
System.out.println("========compareTo方法========");
System.out.println(spring.compareTo(Season.WINTER));
System.out.println("===========================");
}
}
2.枚舉類對接口的實(shí)現(xiàn)方式
準(zhǔn)備工作
新建一個EnumInf 接口,定義一個抽象方法
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:25
*/
public interface EnumInf {
void show();
}
1.實(shí)現(xiàn)接口,在enum中統(tǒng)一實(shí)現(xiàn)抽象方法
新建一個EnumInf 接口,定義抽象方法 show()
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:25
*/
public interface EnumInf {
void show();
}
新建一個OrderStatus 枚舉類 實(shí)現(xiàn) EnumInf 接口
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:27
*/
public enum OrderStatus implements EnumInf{
SUCCESS(200,"交易成功"),
Fail(500,"交易失敗");
private final int code;
private final String desc;
OrderStatus(int code, String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
/**
* 第一種方式,枚舉統(tǒng)一重寫接口抽象方法
*/
@Override
public void show() {
System.out.println("訂單枚舉對象");
}
}
進(jìn)行測試
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:32
*/
public class OrderStatusTest {
public static void main(String[] args) {
OrderStatus success = OrderStatus.SUCCESS;
success.show();
}
}
輸出結(jié)果
訂單枚舉對象
Process finished with exit code 0
跟我們常用類實(shí)現(xiàn)沒有什么區(qū)別,枚舉也是可以統(tǒng)一實(shí)現(xiàn)的,那如果想針對不同的枚舉對象進(jìn)行不同狀態(tài)的實(shí)現(xiàn)怎么辦呢?比如我們的OA系統(tǒng)、或者電商系統(tǒng)中,根據(jù)不同狀態(tài) 我們需要回寫對應(yīng)的數(shù)據(jù),下面我們就來看看如何實(shí)現(xiàn)。
2.枚舉對象分別實(shí)現(xiàn)接口中的抽象方法
案例跟接口統(tǒng)一實(shí)現(xiàn)一致,我們這邊修改一下OrderStatus 枚舉類,代碼如下
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:27
*/
public enum OrderStatus implements EnumInf{
SUCCESS(200,"交易成功") {
@Override
public void show() {
System.out.println("回寫交易成功狀態(tài)");
}
},
Fail(500,"交易失敗") {
@Override
public void show() {
System.out.println("回寫交易失敗狀態(tài)");
}
};
private final int code;
private final String desc;
OrderStatus(int code, String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
我們再修改下測試類代碼:
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:32
*/
public class OrderStatusTest {
public static void main(String[] args) {
OrderStatus success = OrderStatus.SUCCESS;
success.show();
OrderStatus fail = OrderStatus.Fail;
fail.show();
}
}
輸出結(jié)果
回寫交易成功狀態(tài)
回寫交易失敗狀態(tài)
Process finished with exit code 0
通過這種方式就可以輕而易舉地定義每個枚舉實(shí)例不同的行為方式,也達(dá)到了我們預(yù)期的效果,其實(shí)在開發(fā)過程中根據(jù)枚舉的設(shè)計(jì)和設(shè)計(jì)模式的鋪墊可以極大的簡化我們的業(yè)務(wù)代碼。
3.Enum枚舉類的工具類及應(yīng)用場景
1.EnumSet 和 EnumMap
Java 中提供了兩個方便操作enum的工具類——EnumSet 和 EnumMap。
EnumSet 是枚舉類型的高性能 Set 實(shí)現(xiàn)。它要求放入它的枚舉常量必須屬于同一枚舉類型。
// EnumSet的使用
System.out.println("EnumSet展示");
EnumSet<OrderStatus> errSet = EnumSet.allOf(OrderStatus.class);
for (OrderStatus e : errSet) {
System.out.println(e.name() + " : " + e.ordinal());
}
EnumMap 是專門為枚舉類型量身定做的 Map 實(shí)現(xiàn)。雖然使用其它的 Map 實(shí)現(xiàn)(如HashMap)也能完成枚舉類型實(shí)例到值得映射,但是使用 EnumMap 會更加高效:它只能接收同一枚舉類型的實(shí)例作為鍵值,并且由于枚舉類型實(shí)例的數(shù)量相對固定并且有限,所以 EnumMap 使用數(shù)組來存放與枚舉類型對應(yīng)的值。(計(jì)算機(jī)處理連續(xù)的資源使用局部內(nèi)存效率更高)這使得 EnumMap 的效率非常高。
// EnumMap的使用
System.out.println("EnumMap展示");
EnumMap<StateMachine.Signal, String> errMap = new EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED, "紅燈");
errMap.put(StateMachine.Signal.YELLOW, "黃燈");
errMap.put(StateMachine.Signal.GREEN, "綠燈");
for (Iterator<Map.Entry<StateMachine.Signal, String>> iter =errMap.entrySet().iterator(); iter.hasNext();) {
Map.Entry<StateMachine.Signal, String> entry = iter.next();
System.out.println(entry.getKey().name() + " : " + entry.getValue());
}
2.枚舉類與 Switch 的配合使用
關(guān)于枚舉與switch是個比較簡單的話題,使用switch進(jìn)行條件判斷時,條件參數(shù)一般只能是整型,字符型。而枚舉型確實(shí)也被switch所支持,在java 1.7后switch也對字符串進(jìn)行了支持。
實(shí)踐
新建一個 BizEnum 的java class,代碼如下
package org.taoguoguo;
/**
* @author taoGG
* @description 企業(yè)類型枚舉
* @create 2020-09-13 21:24
*/
public enum BizEnum {
COUNTRIES(101,"國有企業(yè)"),
PRIVETE(102,"私營企業(yè)"),
SOHO(103,"個體單位");
private final int code;
private final String desc;
BizEnum(int code, String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
//根據(jù)編碼獲取當(dāng)前枚舉對象的方法
public static BizEnum getBizTypeByCode(int code){
for (BizEnum bizEnum : BizEnum.values()) {
if(code == bizEnum.getCode()){
return bizEnum;
}
}
return null;
}
}
結(jié)合Switch進(jìn)行測試
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 21:31
*/
public class BizTest {
public static void main(String[] args) {
BizEnum bizType = BizEnum.getBizTypeByCode(101);
switch (bizType){
case COUNTRIES:
System.out.println("國有企業(yè)");
break;
case PRIVETE:
System.out.println("私營企業(yè)");
break;
case SOHO:
System.out.println("個體單位");
break;
default:
System.out.println("創(chuàng)業(yè)中");
}
}
}
輸出結(jié)果:
國有企業(yè)
Process finished with exit code 0
總結(jié)
- jdk 5.0之前我們可以自定義枚舉類,jdk 5.0之后使用
enum關(guān)鍵字定義枚舉類,枚舉類默認(rèn)繼承自java.lang.Enum,使用枚舉類將常量組織起來,便于統(tǒng)一管理。例如錯誤碼、狀態(tài)機(jī)等場景中,較為合適使用枚舉類。 - 枚舉類常用方法介紹及枚舉類實(shí)現(xiàn)抽象類、接口等抽象方法的兩種方式。
- 枚舉常用的工具類及與
switch使用的場景。

浙公網(wǎng)安備 33010602011771號