枚舉類型是Java 5中新增特性的一部分,它是一種特殊的數據類型,之所以特殊是因爲它既是一種類(class)類型卻又比類類型多了些特殊的約束,但是這些約束的存在也造就了枚舉類型的簡潔性、安全性以及便捷性。
<span style="color:red">△有的地方還沒有學的透徹,之後會繼續學習修改更新本文章</span>
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 枚舉的常見方法
2.枚舉類使用
2.1常量
系統裏實現常量的三種方式接口常量、類常量、枚舉常量
2.1.1接口常量
寫法(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;
}
}
2.1.2類常量
雖然有了枚舉,可能是由於設計者習慣問題,還有很多人用的類常量, 定義了類常量,用一個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類型,使用枚舉,能讓我們的代碼可讀性更強。
枚舉是聲明一組命名的常數,當一個變量有幾種可能的取值時,可以將它定義爲枚舉類型。枚舉是將變量的值一一列出來,變量的值只侷限於列舉出來的值的範圍內。
<span style="color:red">△注意:</span>枚舉只是枚舉類型,不能夠賦值操作。如下: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
}
}
資料原文地址: