Java列舉類學習到進階

程式碼牛發表於2018-12-14

列舉型別是Java 5中新增特性的一部分,它是一種特殊的資料型別,之所以特殊是因為它既是一種類(class)型別卻又比類型別多了些特殊的約束,但是這些約束的存在也造就了列舉型別的簡潔性、安全性以及便捷性。

△有的地方還沒有學的透徹,之後會繼續學習修改更新本文章

1.列舉類學習

1.1 定義列舉類

  • 列舉類可以實現一個或多個介面,使用enum定義的列舉類預設繼承了java.lang.Enum類,而不是預設繼承Object類,因此列舉類不能顯示繼承其他父類。其中java.lang.Enum類實現了java.lang.Serializable和java.lang.Comparable兩個介面。 使用enum定義、非抽象的列舉類預設會使用final修飾,因此列舉類不能派生子類。
  • 列舉類的構造器只能使用private訪問控制符,如果省略了構造器的訪問控制符,則預設使用private修飾;如果強制指定訪問控制符,則只能指定private修飾符。
  • 列舉類的所有例項必須在列舉類的第一行顯式列出,否則這個列舉類永遠都不能產生例項。列出這些例項時,系統會自動新增public static final 修飾,無須程式設計師顯式新增。
  • 列舉類預設提供了一個values()方法,該方法可以很方便地遍歷所有的列舉值。

如下定義週一到週日的常量

//Day.class
//列舉型別,使用關鍵字enum
enum Day {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
複製程式碼

相當簡潔,在定義列舉型別時我們使用的關鍵字是enum,與class關鍵字類似,只不過前者是定義列舉型別,後者是定義類型別。

1.2 列舉類的實現原理

我們大概瞭解了列舉型別的定義與簡單使用後,現在有必要來了解一下列舉型別的基本實現原理。實際上在使用關鍵字enum建立列舉型別並編譯後,編譯器會為我們生成一個相關的類,這個類繼承了Java API中的java.lang.Enum類,也就是說通過關鍵字enum建立列舉型別在編譯後事實上也是一個類型別而且該類繼承自java.lang.Enum類。

檢視反編譯Day.class檔案:

//反編譯Day.class
final class Day extends Enum
{
    //編譯器為我們新增的靜態的values()方法
    public static Day[] values()
    {
        return (Day[])$VALUES.clone();
    }
    //編譯器為我們新增的靜態的valueOf()方法,注意間接呼叫了Enum也類的valueOf方法
    public static Day valueOf(String s)
    {
        return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
    }
    //私有建構函式
    private Day(String s, int i)
    {
        super(s, i);
    }
     //前面定義的7種列舉例項
    public static final Day MONDAY;
    public static final Day TUESDAY;
    public static final Day WEDNESDAY;
    public static final Day THURSDAY;
    public static final Day FRIDAY;
    public static final Day SATURDAY;
    public static final Day SUNDAY;
    private static final Day $VALUES[];

    static 
    {    
        //例項化列舉例項
        MONDAY = new Day("MONDAY", 0);
        TUESDAY = new Day("TUESDAY", 1);
        WEDNESDAY = new Day("WEDNESDAY", 2);
        THURSDAY = new Day("THURSDAY", 3);
        FRIDAY = new Day("FRIDAY", 4);
        SATURDAY = new Day("SATURDAY", 5);
        SUNDAY = new Day("SUNDAY", 6);
        $VALUES = (new Day[] {
            MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
        });
    }
}
複製程式碼
  • ①從反編譯的程式碼可以看出編譯器確實幫助我們生成了一個Day類(注意該類是final型別的,將無法被繼承)而且該類繼承自java.lang.Enum類,該類是一個抽象類(稍後我們會分析該類中的主要方法)。
  • ②除此之外,編譯器還幫助我們生成了7個Day型別的例項物件分別對應列舉中定義的7個日期,這也充分說明了我們前面使用關鍵字enum定義的Day型別中的每種日期列舉常量也是實實在在的Day例項物件,只不過代表的內容不一樣而已。注意編譯器還為我們生成了兩個靜態方法,分別是values()和 valueOf()
  • ③到此我們也就明白了,使用關鍵字enum定義的列舉型別,在編譯期後,也將轉換成為一個實實在在的類,而在該類中,會存在每個在列舉型別中定義好變數的對應例項物件,如上述的MONDAY列舉型別對應public static final Day MONDAY;,同時編譯器會為該類建立兩個方法,分別是values()和valueOf()。到此相信我們對列舉的實現原理也比較清晰。下面我們深入瞭解一下java.lang.Enum類以及values()和valueOf()的用途。

1.3 列舉的常見方法

Java列舉類學習到進階

2.列舉類使用

2.1常量

系統裡實現常量的三種方式介面常量、類常量、列舉常量

2.1.1介面常量

如java的swing裡有一個SwingConstant:

public interface SwingConstants {

        /** 
         * The central position in an area. Used for
         * both compass-direction constants (NORTH, etc.)
         * and box-orientation constants (TOP, etc.).
         */
        public static final int CENTER  = 0;

        // 
        // Box-orientation constant used to specify locations in a box.
        //
        /** 
         * Box-orientation constant used to specify the top of a box.
         */
        public static final int TOP     = 1;
        /** 
         * Box-orientation constant used to specify the left side of a box.
         */
        public static final int LEFT    = 2;
       
       //。。。省略其他程式碼
   }
複製程式碼

2.1.2類常量

寫法(1)利弊:用到 DefaultValues.DEFAULT_AP 的含義,必須看類裡的註釋,知道他表示中心。如果常量很多的話,把所有的常量都放在這一個介面裡邊,這種方式感覺也不是很友好。

/**
 * 系統預設值
 *
 */
public class DefaultValues {

	/**
	 * 預設密碼
	 */
	public static final String DEFAULT_PASSWORD = "000000";
	/**
	 * 預設使用者型別
	 */
	public static final String DEFAULT_USER_TYPE = UserType.NormalUser.value();	
	/**
	 * 預設獲取api名稱
	 */
	public static final String DEFAULT_API = "api";
	
	/**
	 * 預設系統字元編碼
	 */
	public static final String DEFAULT_ENCODING = "UTF-8";
	
	/**叢集規模*/
	public static final  long CLUSTER_SIZE = 1000;
}
複製程式碼

寫法(2)利弊:公司的介面常量是在介面裡定義靜態內部類,他可以把不同的功能的常量類進一步分類。把不同功能的常量放在了介面的內部類裡,通過不同的內部類可以清楚的知道一個常量的含義。

public class Constants {
	public static class MimeType{
		public static final String BIN = "application/octet-stream";
		public static final String CSS = "text/css";
		public static final String DOC = "application/msword";
		public static final String DOCX = "";
		public static final String EXE = "application/octet-stream";
		public static final String GTAR = "application/x-gtar";
		public static final String GZ = "application/x-gzip";
		public static final String HTM = "text/html;charset=utf-8";
		public static final String ICO = "image/x-icon";
		public static final String JPEG = "image/jpeg";
		public static final String JPG = "image/jpeg";
		public static final String JS = "application/x-javascript;charset=utf-8";
		public static final String JSON = "application/json;charset=utf-8";
		public static final String FORM = "application/x-www-form-urlencoded; charset=UTF-8";
		public static final String MULTIPART = "multipart/form-data; charset=UTF-8";
		public static final String MHT = "message/rfc822";
		public static final String MHTML = "message/rfc822";
		public static final String MOV = "video/quicktime";
		public static final String MP3 = "audio/mpeg";
		public static final String MPE = "video/mpeg";
		public static final String MPEG = "video/mpeg";
		public static final String MPG = "video/mpeg";
		public static final String PDF = "application/pdf";
		public static final String PPT = "application/vnd.ms-powerpoint";
		public static final String RTF = "application/rtf";
		public static final String SWF = "application/x-shockwave-flash";
		public static final String TAR = "application/x-tar";
		public static final String TXT = "text/plain;charset=utf-8";
		public static final String WAV = "audio/x-wav";
		public static final String XML = "text/xml;charset=utf-8";
		public static final String ZIP = "application/zip";
		
	}
	
	public static class DataState{
		public static final String FLAG_REMOVE = "Y";
		public static final String FLAG_NORMAL = "N";
	}
	
	/**
	 * 應用伺服器例項執行狀態
	 */
	public static class ServerASInstanceState{
		public static final int RUNNING = 1;
		public static final int SHUT_OFF = 2;
	}
	/**
	 * WebServices介面分析
	 */
	public static class WebServicesType{
		/**先接收資料,在返回介面情況的介面 **/ 
		public static final String IN_OUT = "IO";
		/**先發資料請求,後返回資料的介面 **/ 
		public static final String OUT_IN = "OI";
		/**只傳送資料的介面**/ 
		public static final String OUT= "O";
		/**只接收資料的介面 **/ 
		public static final String IN = "I";
	}
	
	/**
	 * 任務排程使用
	 */
	public static class TaskScheduling{
		/**任務ID **/ 
		public static final String TASK_ID = "taskID";
		/**任務URL **/ 
		public static final String TASK_URI = "taskURI";
		/**任務URL **/ 
		public static final String TASK_NAME = "taskName";
		/**任務目標伺服器IP **/ 
		public static final String TASK_SERVER_IP = "taskServerIp";
		/**任務目標伺服器IP **/ 
		public static final String TASK_SERVER_PORT = "taskServerPort";
		
		/**任務狀態啟用**/
		public static final int TASK_ENABLED = 1;
		
		/**任務狀態禁用**/
		public static final int TASK_DISABLE = 0;
		
		/**每年任務**/
		public static final int TYPE_EVERY_YEAR= 1;
		
		/**每月任務**/
		public static final int TYPE_EVERY_MONTH = 2;
		
		/**每日任務**/
		public static final int TYPE_EVERY_DAY = 3;
		
		/**每週任務**/
		public static final int TYPE_EVERY_WEEK = 4;
		
		/**單次任務**/
		public static final int TYPE_SINGLE = 5;
		
	}
}
複製程式碼

雖然有了列舉,可能是由於設計者習慣問題,還有很多人用的類常量, 定義了類常量,用一個Map<Integer, String>來封裝常量對應的資訊,在static程式碼塊裡,類初始化的時候執行一次put。用的時候 ResponseCode.RESP_INFO.get("DATABASE_EXCEPTION");就能取出響應資訊 由於專案是前後端分離,在介面文件裡需要寫上狀態碼,還得寫上狀態碼對應的提示資訊,而且我們的響應類 RespInfo 有message屬性,就是儲存常量類裡狀態碼對應的資訊的。

public class ResponseCode {

    /** 系統處理正常 */
    public static final int SUCCESS_HEAD = 0;

    /** 系統處理未知異常 */
    public static final int EXCEPTION_HEAD = 1;

    /** JSON解析錯誤 */
    public static final int JSON_RESOLVE = 2;

    /** 型別不匹配 */
    public static final int TRANSTYPE_NO = 3;

    /** Head - messageID未賦值 */
    public static final int HEAD_messageID = 4;

    /** Head - timeStamp未賦值 */
    public static final int HEAD_timeStamp = 5;

    /** Head - messengerID未賦值 */
    public static final int HEAD_messengerID = 6;

    /** Head - transactionType 未賦值 */
    public static final int HEAD_transactionType = 7;

    /** digest校驗不通過 */
    public static final int HEAD_DIGEST = 8;
    
    /** src校驗不通過 */
    public static final int HEAD_SRC_NULL = 10;
    
    /** 協議包含非法字元 */
    public static final int ILLEGAL_MESSAGE = 11;

    /** 資料庫異常 */
    public static final int DATABASE_EXCEPTION = 9;
    public static final Map<Integer, String> RESP_INFO = new HashMap<Integer, String>();

    static {
        // Head 相關
        RESP_INFO.put(SUCCESS_HEAD, "系統處理正常");
        RESP_INFO.put(EXCEPTION_HEAD, "系統處理未知異常");
        RESP_INFO.put(JSON_RESOLVE, "JSON解析錯誤");
        RESP_INFO.put(TRANSTYPE_NO, "型別不匹配");
        RESP_INFO.put(HEAD_messageID, "messageID未賦值");
        RESP_INFO.put(HEAD_timeStamp, "timeStamp未賦值");
        RESP_INFO.put(HEAD_messengerID, "messengerID未賦值");
        RESP_INFO.put(HEAD_transactionType, "transactionType未賦值");
        RESP_INFO.put(HEAD_DIGEST, "digest校驗不通過");
        RESP_INFO.put(DATABASE_EXCEPTION, "資料庫異常");
        RESP_INFO.put(HEAD_SRC_NULL, "src未賦值");
        RESP_INFO.put(ILLEGAL_MESSAGE, "協議包含非法字元");
        
    }
}
複製程式碼

2.1.3列舉常量

所有的列舉類都是Enum類的子類,就行Object類一樣,只是沒有寫出來,所以可以列舉類可呼叫Enum的方法。注意是逗號分隔屬性,只有屬性後邊沒有方法的話,最後加不加分號都行。

寫法(1)

public enum StateType {
	/**
	 * 成功返回狀態
	 */
	OK(200,"OK"), 
	
	/**
	 * 請求格式錯誤
	 */
	BAD_REQUEST(400,"bad request"),
	/**
	 * 未授權
	 */
	UNAUTHORIZED(401,"unauthorized"),
	/**
	 * 沒有許可權
	 */
	FORBIDDEN(403,"forbidden"),
	
	/**
	 * 請求的資源不存在
	 */
	NOT_FOUND(404,"not found"),
	/**
	 * 該http方法不被允許
	 */
	NOT_ALLOWED(405,"method not allowed"),
	/**
	 * 請求處理髮送異常
	 */
	PROCESSING_EXCEPTION(406,"Handling Exceptions"),
	/**
	 * 
	 * 請求處理未完成
	 */
	PROCESSING_UNFINISHED(407,"To deal with unfinished"),
	
	/**
	 * 登入過期
	 */
	BEOVERDUE(408,"Be overdue"),
	
	/**
	 * 使用者未登入
	 */
	NOT_LOGIN(409,"Not logged in"),
	
	/**
	 * 這個url對應的資源現在不可用
	 */
	GONE(410,"gone"),
	/**
	 * 請求型別錯誤
	 */
	UNSUPPORTED_MEDIA_TYPE(415,"unsupported media type"),
	/**
	 * 校驗錯誤時用
	 */
	UNPROCESSABLE_ENTITY(422,"unprocessable entity"),
	/**
	 * 請求過多
	 */
	TOO_MANY_REQUEST(429,"too many request");

	private int code;
	private String value = null;

	private StateType(int code,String value) {
		this.code = code;
		this.value = value;
	}

	public String value() {
		return this.value;
	}

	public int getCode() {
		return code;
	}

	public static Boolean isValidateStateType(String... stateType) {
		for (int i = 0; i < stateType.length; i++) {
			StateType [] value = StateType.values();
			boolean falg = false;
			for(StateType type : value) {
				if(type.value.equals(stateType[i])) {
					falg = true;
				}
				
			}
			if(!falg) {
				return falg;
			}
		}
		return true;
	}
}

/*使用*/
public static void main(String[] args) {
	System.out.println("狀態碼:"+StateType.getCode());
        System.out.println("錯誤資訊:"+StateType.getValue());
}
複製程式碼

寫法(2)

public enum Level {
	/**
	 * 第一層
	 */
	One(1),
	/**
	 * 第二層 
	 */
	Two(2),
	/**
	 * 第三層
	 */
	Three(3),
	/**
	 * 第四層
	 */
	Four(4),
	/**
	 * 第五層
	 */
	Five(5);
	
	private int value;
	
	Level(int value) {
		this.value = value;
	}
	
	public int value() {
		return this.value;
	}
	
	public static Boolean isValidateLevel(int level) {
		Level [] value = Level.values();
		boolean falg = false;
		for (Level pl : value){
			if(pl.value == level){
				falg = true;
			}
		}
		return falg;
	}
}

/*使用*/
public static void main(String[] args) {
	System.out.println("樓層:"+Level.Three);
}
複製程式碼

2.2 switch結合列舉類

JDK1.6之前的switch語句只支援int,char,enum型別,使用列舉,能讓我們的程式碼可讀性更強。

列舉是宣告一組命名的常數,當一個變數有幾種可能的取值時,可以將它定義為列舉型別。列舉是將變數的值一一列出來,變數的值只侷限於列舉出來的值的範圍內。

△注意:列舉只是列舉型別,不能夠賦值操作。如下:GREEN預設值為0,但是GREEN不能=0,因為資料型別不一樣。列舉中變數未直接賦值,預設等於前一個變數值加一,起始值預設為0。

enum Signal { 
 GREEN, YELLOW, RED 
} 
public class TrafficLight { 
 Signal color = Signal.RED; 
 public void change() { 
  switch (color) { 
  case RED: 
   color = Signal.GREEN; 
   break; 
  case YELLOW: 
   color = Signal.RED; 
   break; 
  case GREEN: 
   color = Signal.YELLOW; 
   break; 
  } 
 } 
} 
複製程式碼

2.3 向列舉中新增新的方法

如果打算自定義自己的方法,那麼必須在enum例項序列的最後新增一個分號(“;”),java中要求必須先定義java例項。

public enum ChannelEnum {

    MSG_CENTER_CHANNEL1("msg_center_channel1"),
    MSG_CENTER_CHANNEL("msg_center_channel");

    private String channel = null;

    private ChannelEnum(String channel) {
        this.channel = channel;
    }

    public String getChannel() {
        return this.channel;
    }
}
複製程式碼

2.4 實現介面

  • 所有的列舉都繼承自java.lang.Enum類。由於Java 不支援多繼承,所以列舉物件不能再繼承其他類。
  • 如果由列舉類來實現介面裡的方法,則每個列舉值在呼叫該方法時都有相同的行為方式(因為方法體完全一樣)。如果需要每個列舉值在呼叫該方法時呈現出不同的行為方式,則可以讓每個列舉值分別來實現該方法,每個列舉值提供不同的實現方式,從而讓不同的列舉值呼叫該方法時具有不同的行為方式。
public interface Behaviour {  
    void print();  
    String getInfo();  
}  
public enum Color implements Behaviour{  
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);  
    // 成員變數  
    private String name;  
    private int index;  
    // 構造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
//介面方法  
    @Override  
    public String getInfo() {  
        return this.name;  
    }  
    //介面方法  
    @Override  
    public void print() {  
        System.out.println(this.index+":"+this.name);  
    }  
}
複製程式碼

2.5 使用介面組織列舉

public interface Food {
    enum Coffee implements Food{  
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
    }  
    enum Dessert implements Food{  
        FRUIT, CAKE, GELATO  
    }  
}
複製程式碼

資料原文地址:

相關文章