Java SE 枚舉類
作者: [霍英俊] ([email protected])
文章目錄
- Java SE 枚舉類
-
- 1、枚舉類了解
- 2、自定義枚舉類(jdk5.0前)
- 3、使用接口定義靜态常量
- 4、使用enum定義枚舉類(jdk5.0新增)(重點)
-
- 1 、說明介紹
- 2、簡單枚舉
- 3、 自定義擴充枚舉
- 4、Enum類的主要方法
- 5、 實作接口的枚舉類
- 5、枚舉與設計模式
-
- 1、單例模式
- 2、政策模式
- 6、專門用于枚舉的集合類
-
- 1、EnumSet
- 2、EnumMap
1、枚舉類了解
枚舉類型可以取代以往常量的定義方式,即将常量封裝在類或者接口中,此外,它還賦予程式在編譯時進行安全檢查的功能。枚舉類型本質上還是以類的形式存在。
前提:類的對象是有限個的,确定的。當需要定義一組常量時,強烈建議使用枚舉類
若枚舉隻有一個對象, 則可以作為一種單例模式的實作方式。
- 枚舉類對象的屬性不應允許被改動, 是以應該使用 private final 修飾
- 枚舉類的使用 private final 修飾的屬性應該在構造器中為其指派
- 若枚舉類顯式的定義了帶參數的構造器, 則在列出枚舉值時也必須對應的 傳入參數
2、自定義枚舉類(jdk5.0前)
代碼實作:
package com.huo.demo.enums;
/**
* @author 霍英俊
* @version 1.0.0
* @className: SeasonEnumDemo01
* @description: TODO
* @email [email protected]
* @date 2020/7/5 20:57
*/
/**
* 自定義枚舉類舉例 jdk5.0前
*/
public class SeasonEnumDemo01 {
//1.private final 修飾對象屬性
private final String SEASONNAME;
private final String SEASONDESC;
//2.私有化類的構造器,被初始化對象
private SeasonEnumDemo01(String seasonName, String seasonDesc){
this.SEASONNAME = seasonName;
this.SEASONDESC = seasonDesc;
}
//3.提供目前枚舉類的多個對象:public static final修飾
public static SeasonEnumDemo01 SPRING = new SeasonEnumDemo01("春天","春暖花開");
public static SeasonEnumDemo01 SUMMER = new SeasonEnumDemo01("夏天","夏日炎炎");
public static SeasonEnumDemo01 AUTOMN = new SeasonEnumDemo01("秋天","秋高氣爽");
public static SeasonEnumDemo01 WINTER = new SeasonEnumDemo01("冬天","白雪皚皚");
//擷取枚舉類的屬性
public String getSEASONNAME(){
return this.SEASONNAME;
}
public String getSEASONDESC(){
return this.SEASONDESC;
}
//提供toString()
@Override
public String toString() {
return "SeasonEnumDemo01{" +
"SEASONNAME='" + SEASONNAME + '\'' +
", SEASONDESC='" + SEASONDESC + '\'' +
'}';
}
}
測試:
package com.huo.demo.enums;
public class EnumTest {
public static void main(String[] args) {
System.out.println(SeasonEnumDemo01.SPRING);
System.out.println(SeasonEnumDemo01.SPRING.getSEASONNAME());
System.out.println(SeasonEnumDemo01.SPRING.getSEASONDESC());
}
}
---
SeasonEnumDemo01{SEASONNAME='春天', SEASONDESC='春暖花開'}
春天
春暖花開
3、使用接口定義靜态常量
舉個栗子,就以網站上傳視訊為例,視訊一般有三個狀态:草稿、稽核和釋出,我們可以将其定義為靜态常量:
public class VideoStatus {
public static final int Draft = 1; //草稿
public static final int Review = 2; //稽核
public static final int Published = 3; //釋出
}
對于這種單值類型的靜态常量定義,本身也沒錯,主要是在使用的地方沒有一個明确性的限制而已,比如:
void judgeVideoStatus( int status ) {
...
}
比如這裡的
judgeVideoStatus
函數的本意是傳入
VideoStatus
的三種靜态常量之一,但由于沒有類型上的限制,是以傳入任意一個
int
值都是可以的,編譯器也不會提出任何警告。
但是在枚舉類型出現之後,上面這種情況就可以用枚舉嚴謹地去限制,比如用枚舉去定義視訊狀态就非常簡潔了:
public enum VideoStatus {
Draft, Review, Published
}
而且主要是在用枚舉的地方會有更強的類型限制:
// 入參就有明确類型限制
void judgeVideoStatus( VideoStatus status ) {
...
}
這樣在使用
judgeVideoStatus
函數時,入參類型就會受到明确的類型限制,一旦傳入無效值,編譯器就會幫我們檢查,進而規避潛在問題。
除此之外,枚舉在擴充性方面比普常量更友善、也更優雅。
4、使用enum定義枚舉類(jdk5.0新增)(重點)
1 、說明介紹
- 使用 enum 定義的枚舉類預設繼承了 java.lang.Enum 類,是以不能再繼承其他類
- 枚舉類的構造器隻能使用 private 權限修飾符
- 枚舉類的所有執行個體必須在枚舉類中顯式列出(, 分隔 ; 結尾)。列出的執行個體系統會自動添加 public static final 修飾
- 必須在枚舉類的第一行聲明枚舉類對象
2、簡單枚舉
public enum UserRole {
ROLE_ROOT_ADMIN, // 系統管理者
ROLE_ORDER_ADMIN, // 訂單管理者
ROLE_NORMAL // 普通使用者
}
3、 自定義擴充枚舉
package com.huo.demo.enums;
/**
* 使用enum定義枚舉類
* @author 霍英俊
* @version 1.0.0
* @className: SeasonEnumDemo01
* @description: TODO
* @email [email protected]
* @date 2020/7/5 20:57
*/
public enum SeasonEnumDemo02 {
//1.提供目前枚舉類的多個對象:多個對象間用“,”隔開,最後一個對象“;”結束
SPRING("春天","春暖花開"),
SUMMER("夏天","夏日炎炎"),
AUTOMN("秋天","秋高氣爽"),
WINTER("冬天","白雪皚皚");
//2.private final 修飾對象屬性
private final String seasonName;
private final String seasonDesc;
//3.私有化類的構造器,被初始化對象
private SeasonEnumDemo02(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//擷取枚舉類的屬性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
// 不需要單獨重寫 toString(), java.lang.Enum類重寫的toString()
}
測試:
package com.huo.demo.enums;
/**
* 使用enum定義枚舉類
* @author 霍英俊
* @version 1.0.0
* @className: EnumTest
* @description: TODO
* @email [email protected]
* @date 2020/7/5 21:04
*/
public class EnumTest {
public static void main(String[] args) {
System.out.println(SeasonEnumDemo02.WINTER);
System.out.println(SeasonEnumDemo02.WINTER.getSeasonName());
System.out.println(SeasonEnumDemo02.WINTER.getSeasonDesc());
}
}
---
WINTER
冬天
白雪皚皚
4、Enum類的主要方法
-
values()方法:
傳回枚舉類型的對象數組。該方法可以很友善地周遊所有的 枚舉值。
-
valueOf(String str):
可以把一個字元串轉為對應的枚舉類對象。要求字元串必須是枚舉類對象的“名字”。如不是,會有運作時異常: IllegalArgumentException。
-
toString():
傳回目前枚舉類對象常量的名稱
-
ordinal()方法:
傳回枚舉常量的序數,注意從0開始
-
name()方法:
獲得枚舉常量的名稱
…
package com.huo.demo.enums;
/**
* 使用enum定義枚舉類
*
* @author 霍英俊
* @version 1.0.0
* @className: EnumTest
* @description: TODO
* @email [email protected]
* @date 2020/7/5 21:04
*/
public class EnumTest {
public static void main(String[] args) {
UserRole role1 = UserRole.ROLE_ROOT_ADMIN;
UserRole role2 = UserRole.ROLE_ORDER_ADMIN;
UserRole role3 = UserRole.ROLE_NORMAL;
// values()方法:傳回所有枚舉常量的數組集合
for (UserRole role : UserRole.values()) {
System.out.println(role);
}
// 列印:
// ROLE_ROOT_ADMIN
// ROLE_ORDER_ADMIN
// ROLE_NORMAL
// ordinal()方法:傳回枚舉常量的序數,注意從0開始
System.out.println(role1.ordinal()); // 列印0
System.out.println(role2.ordinal()); // 列印1
System.out.println(role3.ordinal()); // 列印2
// compareTo()方法:枚舉常量間的比較
System.out.println(role1.compareTo(role2)); //列印-1
System.out.println(role2.compareTo(role3)); //列印-2
System.out.println(role1.compareTo(role3)); //列印-2
// name()方法:獲得枚舉常量的名稱
System.out.println(role1.name()); // 列印ROLE_ROOT_ADMIN
System.out.println(role2.name()); // 列印ROLE_ORDER_ADMIN
System.out.println(role3.name()); // 列印ROLE_NORMAL
// valueOf()方法:傳回指定名稱的枚舉常量
System.out.println(UserRole.valueOf("ROLE_ROOT_ADMIN"));
System.out.println(UserRole.valueOf("ROLE_ORDER_ADMIN"));
System.out.println(UserRole.valueOf("ROLE_NORMAL"));
}
}
除此之外,枚舉還可以用于
switch
語句中,而且意義更加明确:
UserRole userRole = UserRole.ROLE_ORDER_ADMIN;
switch (userRole) {
case ROLE_ROOT_ADMIN: // 比如此處的意義就非常清晰了,比1,2,3這種數字好!
System.out.println("這是系統管理者角色");
break;
case ROLE_ORDER_ADMIN:
System.out.println("這是訂單管理者角色");
break;
case ROLE_NORMAL:
System.out.println("這是普通使用者角色");
break;
}
5、 實作接口的枚舉類
和普通 Java 類一樣,枚舉類可以實作一個或多個接口
- 若每個枚舉值在調用實作的接口方法呈現相同的行為方式,則隻要統一實作該方法即可。
- 若需要每個枚舉值在調用實作的接口方法呈現出不同的行為方式, 則可以讓每個枚舉值分别來實作該方法
代碼實作:
接口:
package com.huo.demo.enums;
public interface Info {
void show();
}
統一實作接口方法:
package com.huo.demo.enums;
public enum SeasonEnumDemo03 implements Info{
//1.提供目前枚舉類的多個對象:多個對象間用“,”隔開,最後一個對象“;”結束
SPRING("春天","春暖花開"),
SUMMER("夏天","夏日炎炎"),
AUTOMN("秋天","秋高氣爽"),
WINTER("冬天","白雪皚皚");
//2.private final 修飾對象屬性
private final String seasonName;
private final String seasonDesc;
//3.私有化類的構造器,被初始化對象
private SeasonEnumDemo03(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//擷取枚舉類的屬性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public void show() {
System.out.println("這是實作了Info#show()");
}
// 不需要單獨重寫 toString(), java.lang.Enum類重寫的toString()
}
每個枚舉值實作接口方法:
package com.huo.demo.enums;
public enum SeasonEnumDemo04 implements Info{
//1.提供目前枚舉類的多個對象:多個對象間用“,”隔開,最後一個對象“;”結束
SPRING("春天","春暖花開"){
@Override
public void show() {
System.out.println("這是春天的show()");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("這是夏天的show()");
}
},
AUTOMN("秋天","秋高氣爽"){
@Override
public void show() {
System.out.println("這是秋天的show()");
}
},
WINTER("冬天","白雪皚皚"){
@Override
public void show() {
System.out.println("這是冬天的show()");
}
};
//2.private final 修飾對象屬性
private final String seasonName;
private final String seasonDesc;
//3.私有化類的構造器,被初始化對象
private SeasonEnumDemo04(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//擷取枚舉類的屬性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
// 不需要單獨重寫 toString(), java.lang.Enum類重寫的toString()
}
5、枚舉與設計模式
什麼?枚舉還能實作設計模式?
是的!不僅能而且還能實作好幾種!
1、單例模式
public class Singleton {
// 構造函數私有化,避免外部建立執行個體
private Singleton() {
}
//定義一個内部枚舉
public enum SingletonEnum{
SEED; // 唯一一個枚舉對象,我們稱它為“種子選手”!
private Singleton singleton;
SingletonEnum(){
singleton = new Singleton(); //真正的對象建立隐蔽在此!
}
public Singleton getInstnce(){
return singleton;
}
}
// 故意外露的對象擷取方法,也是外面擷取執行個體的唯一入口
public static Singleton getInstance(){
return SingletonEnum.SEED.getInstnce(); // 通過枚舉的種子選手來完成
}
}
2、政策模式
這個也比較好舉例,比如用枚舉就可以寫出一個基于政策模式的加減乘除電腦
public class Test {
public enum Calculator {
ADDITION {
public Double execute( Double x, Double y ) {
return x + y; // 加法
}
},
SUBTRACTION {
public Double execute( Double x, Double y ) {
return x - y; // 減法
}
},
MULTIPLICATION {
public Double execute( Double x, Double y ) {
return x * y; // 乘法
}
},
DIVISION {
public Double execute( Double x, Double y ) {
return x/y; // 除法
}
};
public abstract Double execute(Double x, Double y);
}
public static void main(String[] args) {
System.out.println( Calculator.ADDITION.execute( 4.0, 2.0 ) );
// 列印 6.0
System.out.println( Calculator.SUBTRACTION.execute( 4.0, 2.0 ) );
// 列印 2.0
System.out.println( Calculator.MULTIPLICATION.execute( 4.0, 2.0 ) );
// 列印 8.0
System.out.println( Calculator.DIVISION.execute( 4.0, 2.0 ) );
// 列印 2.0
}
}
6、專門用于枚舉的集合類
我們平常一般習慣于使用諸如:
HashMap
和
HashSet
等集合來盛放元素,而對于枚舉,有它專門的集合類:
EnumSet
和
EnumMap
1、EnumSet
EnumSet
是專門為盛放枚舉類型所設計的
Set
類型。
還是舉例來說,就以上文中開頭定義的角色枚舉為例:
public enum UserRole {
ROLE_ROOT_ADMIN, // 系統管理者
ROLE_ORDER_ADMIN, // 訂單管理者
ROLE_NORMAL // 普通使用者
}
比如系統裡來了一批人,我們需要檢視他是不是某個角色中的一個:
// 定義一個管理者角色的專屬集合
EnumSet<UserRole> userRolesForAdmin
= EnumSet.of(
UserRole.ROLE_ROOT_ADMIN,
UserRole.ROLE_ORDER_ADMIN
);
// 判斷某個進來的使用者是不是管理者
Boolean isAdmin( User user ) {
if( userRoles.contains( user.getUserRole() ) )
return true;
return false;
}
2、EnumMap
同樣,
EnumMap
則是用來專門盛放枚舉類型為
key
的
Map
類型。
比如,系統裡來了一批人,我們需要統計不同的角色到底有多少人這種的話:
Map<UserRole,Integer> userStatisticMap = new EnumMap<>(UserRole.class);
for ( User user : userList ) {
Integer num = userStatisticMap.get( user.getUserRole() );
if( null != num ) {
userStatisticMap.put( user.getUserRole(), num+1 );
} else {
userStatisticMap.put( user.getUserRole(), 1 );
}
}
用
EnumMap
可以說非常友善了。