Java枚舉深度解讀,看這篇就夠了
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|??濤GuoGuo的跟屁蟲丶博Ke?
來源 |? urlify.cn/aaamQf
66套java從入門到精通實(shí)戰(zhàn)課程分享
Java枚舉
1、枚舉類概念的理解與定義
一個(gè)類的對(duì)象是有限個(gè),確定的,我們稱此為枚舉類。
當(dāng)需要定義和維護(hù)一組常量時(shí),強(qiáng)烈建議使用枚舉類。
如果一個(gè)枚舉類中只有一個(gè)對(duì)象,則可以作為單例模式的實(shí)現(xiàn)方式。
2、枚舉類的定義
關(guān)于枚舉類的定義,這塊主要想和大家分享兩種方式
jdk 5.0之前,自定義枚舉類方式
jdk 5.0之后,Enum關(guān)鍵字方式定義
3、實(shí)踐
(一)、準(zhǔn)備工作
我們新建一個(gè) Java Project ,并創(chuàng)建一個(gè)包,以及一個(gè)測試類
(二)、自定義枚舉的三種方式(jdk 5.0 之前)
1. 定義一個(gè)抽象類,在抽象類中定義常量進(jìn)行維護(hù),我們接下來以 Java 類庫中的 Calendar 類示例來進(jìn)行說明
新建一個(gè)類 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)過來,這個(gè)是獲取當(dāng)前的年份,類似的值還有
3?-?一年中的第幾個(gè)星期
4?-?一年中的第幾個(gè)月
5?-?當(dāng)前的日期?
......但是這么多值,我們怎么能記得住呢?萬一我輸入錯(cuò)誤,隨便取了一個(gè)范圍怎么辦?
沒錯(cuò),這是 jdk 5.0之前的痛點(diǎn),為了解決實(shí)例數(shù)量固定,便于維護(hù)這些問題,在jdk 5.0之后更新Enum枚舉類解決了這個(gè)問題。那在jdk 5.0之前官方是怎么做的呢?難道需要我們一個(gè)個(gè)去記住?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é)果與之前一致,這時(shí)我們就清楚,在開發(fā)過程中作為開發(fā)者我們肯定愿意使用?Calendar.YEAR?這種寫法,一來方便記憶,二來可讀性高。那么官方的做法時(shí)怎樣的呢?我們點(diǎn)進(jìn)去源碼看一下
首先?Calendar?本身是一個(gè)抽象類,實(shí)現(xiàn)了序列化、克隆、以及比較排序接口,這邊和我們枚舉沒有太大關(guān)系,我們繼續(xù)往下看

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

2. 定義一個(gè)接口,在接口中定義常量維護(hù)枚舉值
我們新建一個(gè)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ù)常量相同的目的。上面這兩種做法都非常的簡單易用,但也有弊端。比如我們只知道一個(gè)狀態(tài)值,當(dāng)我們要獲取狀態(tài)的屬性或者相關(guān)的內(nèi)容時(shí),我們該怎么做呢?
下面我們使用第三種方式,自定義枚舉類,這種基本上達(dá)到和 Enum 關(guān)鍵字相同的作用,但有一點(diǎn)不足就是會(huì)較為復(fù)雜
3.自定義枚舉類,通過為類私有化構(gòu)造器和固定實(shí)例對(duì)象進(jìn)行枚舉維護(hù)
新建一個(gè)class SeasonEnum.java,代碼如下:
package?org.taoguoguo;
/**
?*?@author?taoGG
?*?@description
?*?@create?2020-09-13?15:58
?*/
public?class?SeasonEnum?{
????//1.聲明枚舉對(duì)象的屬性
????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)前枚舉類的多個(gè)對(duì)象?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?+
????????????????'}';
????}
}新建一個(gè)class SeasonEnumTest 進(jìn)行測試,當(dāng)我們通過自定義枚舉類引用實(shí)例對(duì)象時(shí),如下圖可以看到,我們已經(jīng)可以獲取到我們的枚舉對(duì)象了。
獲取到枚舉對(duì)象,我們當(dāng)然也可以獲取到對(duì)應(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("自定義枚舉類對(duì)象:"?+?spring);
????????System.out.println("自定義枚舉類屬性:"?+?spring.getSeasonName());
????????System.out.println("自定義枚舉類屬性:"?+?spring.getCode());
????}
}
根據(jù)我們上面的自定義枚舉類方式,我們基本已經(jīng)實(shí)現(xiàn)了枚舉的功能了,但是就像上面說到的,如果開發(fā)中枚舉類型較多,開發(fā)多個(gè)這樣的自定義枚舉類會(huì)非常的耗時(shí),所以 jdk 5.0 之后,推出了 Enum 關(guān)鍵字定義枚舉類
(三)Enum 關(guān)鍵字定義枚舉類(jdk 5.0之后)
enum?全稱為?enumeration,是jdk 5.0 中引入的新特性,在Java 中被?enum?關(guān)鍵字修飾的類型就是枚舉類型
我們通過代碼來示例來講解和理解?enum?的用法,還是用我們剛剛自定以枚舉類的例子,看看使用enum如何來寫
新建一個(gè)Java class ,Kind?類型選擇?enum?如圖:
枚舉類創(chuàng)建注意:
枚舉實(shí)例必須在?
enum關(guān)鍵字聲明的類中顯式的指定(首行開始的以第一個(gè)分號(hào)結(jié)束)枚舉不允許使用new,clone,反射,序列化手動(dòng)創(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方法,會(huì)默認(rèn)使用Enum類本身提供的 toString 方法,返回枚舉類名稱,因?yàn)槎x的枚舉類默認(rèn)隱式繼承于java.lang.Enum
1.枚舉類主要方法介紹
values()??:該方法可以返回當(dāng)前枚舉類型的對(duì)象數(shù)組,可以很方便的遍歷所有枚舉值。一般我們可以根據(jù)枚舉類的相關(guān)屬性通過此方法遍歷獲取對(duì)應(yīng)的枚舉對(duì)象及枚舉值valueOf(String str)?: 根據(jù)枚舉類名稱獲取枚舉類對(duì)象toString(): 默認(rèn)使用 java.lang.Enum的 toString方法,返回當(dāng)前對(duì)象常量的名稱,枚舉類推薦重寫返回自定義友好描述name(): 返回當(dāng)前枚舉對(duì)象名稱,和toString作用上類似,當(dāng)時(shí)toString支持重寫,name方法是不能重寫的,在本質(zhì)上 toString 也是調(diào)用的 name方法,枚舉定義 name 方法就是為了返回枚舉對(duì)象名稱,而 toString 應(yīng)該根據(jù)需要進(jìn)行重寫ordinal(): 返回當(dāng)前枚舉對(duì)象的序號(hào), 實(shí)現(xiàn)了 Comparable 接口,表明它是支持排序的 可以通過?Collections.sort?進(jìn)行自動(dòng)排序比較此枚舉與指定對(duì)象的順序compareTo(): 基于ordinal進(jìn)行序號(hào)大小比較
方式演示代碼,小伙伴們可以自行運(yùn)行輸出一下,看看各個(gè)方法的作用,熟悉一下相關(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.枚舉類對(duì)接口的實(shí)現(xiàn)方式
準(zhǔn)備工作
新建一個(gè)EnumInf?接口,定義一個(gè)抽象方法
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)抽象方法
新建一個(gè)EnumInf?接口,定義抽象方法?show()
package?org.taoguoguo;
/**
?*?@author?taoGG
?*?@description
?*?@create?2020-09-13?17:25
?*/
public?interface?EnumInf?{
????void?show();
}
新建一個(gè)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("訂單枚舉對(duì)象");
????}
}
進(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é)果
訂單枚舉對(duì)象
Process?finished?with?exit?code?0跟我們常用類實(shí)現(xiàn)沒有什么區(qū)別,枚舉也是可以統(tǒng)一實(shí)現(xiàn)的,那如果想針對(duì)不同的枚舉對(duì)象進(jìn)行不同狀態(tài)的實(shí)現(xiàn)怎么辦呢?比如我們的OA系統(tǒng)、或者電商系統(tǒng)中,根據(jù)不同狀態(tài) 我們需要回寫對(duì)應(yīng)的數(shù)據(jù),下面我們就來看看如何實(shí)現(xiàn)。
2.枚舉對(duì)象分別實(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通過這種方式就可以輕而易舉地定義每個(gè)枚舉實(shí)例不同的行為方式,也達(dá)到了我們預(yù)期的效果,其實(shí)在開發(fā)過程中根據(jù)枚舉的設(shè)計(jì)和設(shè)計(jì)模式的鋪墊可以極大的簡化我們的業(yè)務(wù)代碼。
3.Enum枚舉類的工具類及應(yīng)用場景
1.EnumSet 和 EnumMap
Java 中提供了兩個(gè)方便操作enum的工具類——EnumSet 和 EnumMap。
EnumSet?是枚舉類型的高性能?Set?實(shí)現(xiàn)。它要求放入它的枚舉常量必須屬于同一枚舉類型。
//?EnumSet的使用
System.out.println("EnumSet展示");
EnumSet?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 會(huì)更加高效:它只能接收同一枚舉類型的實(shí)例作為鍵值,并且由于枚舉類型實(shí)例的數(shù)量相對(duì)固定并且有限,所以 EnumMap 使用數(shù)組來存放與枚舉類型對(duì)應(yīng)的值。(計(jì)算機(jī)處理連續(xù)的資源使用局部內(nèi)存效率更高)這使得 EnumMap 的效率非常高。
//?EnumMap的使用
System.out.println("EnumMap展示");
EnumMap?errMap?=?new?EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED,?"紅燈");
errMap.put(StateMachine.Signal.YELLOW,?"黃燈");
errMap.put(StateMachine.Signal.GREEN,?"綠燈");
for?(Iterator>?iter?=errMap.entrySet().iterator();?iter.hasNext();)?{
????Map.Entry?entry?=?iter.next();
????System.out.println(entry.getKey().name()?+?"?:?"?+?entry.getValue());
}
2.枚舉類與 Switch 的配合使用
關(guān)于枚舉與switch是個(gè)比較簡單的話題,使用switch進(jìn)行條件判斷時(shí),條件參數(shù)一般只能是整型,字符型。而枚舉型確實(shí)也被switch所支持,在java 1.7后switch也對(duì)字符串進(jìn)行了支持。
實(shí)踐
新建一個(gè)?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,"個(gè)體單位");
????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)前枚舉對(duì)象的方法
????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("個(gè)體單位");
????????????????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)一管理。例如錯(cuò)誤碼、狀態(tài)機(jī)等場景中,較為合適使用枚舉類。枚舉類常用方法介紹及枚舉類實(shí)現(xiàn)抽象類、接口等抽象方法的兩種方式。
枚舉常用的工具類及與
switch使用的場景。
粉絲福利:108本java從入門到大神精選電子書領(lǐng)取
???
?長按上方鋒哥微信二維碼?2 秒 備注「1234」即可獲取資料以及 可以進(jìn)入java1234官方微信群
感謝點(diǎn)贊支持下哈?
