天天看點

Java SE 枚舉類Java SE 枚舉類

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

可以說非常友善了。