Java枚舉的用法和原理深刻

 

轉載請註明原文地址:http://www.javashuo.com/article/p-etnppalp-em.htmlhtml

 

一:枚舉的用法

  一、定義和組織常量   java

  在JDK1.5以前,咱們定義常量都是:public static fianl....。有了枚舉以後,咱們能夠把相關的常量定義到一個枚舉類裏,並且枚舉類也提供了比常量更多的操做方法來操縱。安全

  用法舉例:ide

public enum EnumTest {
    MON, TUE, WED, THU, FRI, SAT, SUN;
}

    看不懂不要緊,上面的枚舉類定義原理在下文中再解釋。函數

   

  二、用於switch測試

      switch語句只支持常量值做爲判斷依據,枚舉類型是個特例。this

  用法舉例:spa

//定義枚舉
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;  
        }  
    }  
}  

 

  三、向枚舉中自定義屬性和方法線程

  咱們在定義枚舉時,實際上是在定義一個Enum類的子類,咱們能夠往其中添加自定義的屬性和方法。code

  可是要注意:咱們須要在枚舉類中先定義enum實例,用分號 ; 隔開。而後纔是成員變量和方法的定義。若是順序錯了會致使編譯錯誤。【實際開發時有可能反過來,先定義成員變量和方法,再添加枚舉實例】

      用法舉例:

public enum Color {  
    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;  
    }  
    // 添加方法  
    public static String getName(int index) {  
        for (Color c : Color.values()) {  
            if (c.getIndex() == index) {  
                return c.name;  
            }  
        }  
        return null;  
    }  
    // get set 方法  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getIndex() {  
        return index;  
    }  
    public void setIndex(int index) {  
        this.index = index;  
    }  
}  

 

  四、重寫Enum類中的方法

  定義枚舉類的過程實際上是定義Enum類的子類的過程,Enum類定義了一些方法,這些方法咱們能夠在本身定義枚舉類時重寫,最多見的是:重寫toString()函數,返回自定義成員變量的拼接結果。

  用法舉例:

public enum Color {  
    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 toString() {  
        return this.index+"_"+this.name;  
    }  
}  

 

  五、實現接口

  前面提到,用enum關鍵字定義枚舉類時,實際上是定義Enum類的子類。也就是說,咱們定義的枚舉類是繼承自 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);  
    }  
}  

 

  六、EnumSet的使用

  java.util.EnumSet是枚舉類型的集合類,能夠保證集合中的枚舉元素不重複。

// EnumSet的使用
EnumSet<EnumTest> weekSet = EnumSet.allOf(aEnum.class); //將枚舉類.class文件做爲參數。至於爲什麼使用.class文件,在下文中揭曉
        

 

  七、EnumMap的使用

  java.util.EnumMap中的 key是enum類型,而value則能夠是任意類型。

 // EnumMap的使用
 EnumMap<自定義Enum類型, String> weekMap = new EnumMap(aEnum.class);

 weekMap.put(aEnum.MON, "星期一");
 weekMap.put(aEnumt.TUE, "星期二");

 

 

二:枚舉的實現——Enum類瞭解

   enum這個關鍵字,包含了:繼承Enum類,定義當前類 的意思,所建立的類型都是 java.lang.Enum 類的子類。

  雖然都是定義類,可是enum關鍵字和class關鍵字的約束行爲不一樣,class定義的類,經過new操做建立對象,想new幾個就幾個,而enum關鍵字定義的類,其實例對象,只能在這個enum類中定義好,它的實例是有限的,限制了某些東西的範圍。

  若是咱們不自定義枚舉類的成員變量和構造方法,只定義枚舉實例,則枚舉實例內容都將以字符串的形式存在,在類加載的時候會經過 protected Enum(String name, int ordinal) 構造函數被建立爲基本的Enum實例。 

  回到第一點中的第一個用法:

public enum EnumTest {
    MON, TUE, WED, THU, FRI, SAT, SUN;
}

  其解釋過程爲:

new Enum<EnumTest>("MON",0);
new Enum<EnumTest>("TUE",1);
new Enum<EnumTest>("WED",2);
    ... ...

  也就是說,這段代碼實際上調用了7次 Enum(String name, int ordinal)。

  枚舉類通過編譯器編譯以後產生的是一個class文件,該class文件通過反編譯能夠看到其實是生成了一個類,該類繼承了java.lang.Enum<E>。 

  也就是說,enum 實際上就是一個 class,只不過 java 編譯器幫咱們作了語法的解析和編譯而已,咱們的枚舉值也被解釋成了static final修飾的常量。

public class com.hmw.test.EnumTest extends java.lang.Enum{
  //咱們能夠看到:定義的時候的枚舉值,被實例化了
public static final com.hmw.test.EnumTest MON; public static final com.hmw.test.EnumTest TUE; public static final com.hmw.test.EnumTest WED; public static final com.hmw.test.EnumTest THU; public static final com.hmw.test.EnumTest FRI; public static final com.hmw.test.EnumTest SAT; public static final com.hmw.test.EnumTest SUN;
static {}; public int getValue(); public boolean isRest(); public static com.hmw.test.EnumTest[] values(); public static com.hmw.test.EnumTest valueOf(java.lang.String); com.hmw.test.EnumTest(java.lang.String, int, int, com.hmw.test.EnumTest); }

  

  Enum類中封裝了一些方法,最經常使用的是 compareTo 和 toString。

int compareTo(E o) 
          比較此枚舉與指定對象的順序。

Class<E> getDeclaringClass() 
          返回與此枚舉常量的枚舉類型相對應的 Class 對象。

String name() 
          返回此枚舉常量的名稱,在其枚舉聲明中對其進行聲明。

int ordinal() 
          返回枚舉常量的序數(它在枚舉聲明中的位置,其中初始常量序數爲零)。

String toString()

           返回枚舉常量的名稱,它包含在聲明中。

static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 
          返回帶指定名稱的指定枚舉類型的枚舉常量。

 

三:使用枚舉實現單例模式

  參考個人另外一片博文;http://www.javashuo.com/article/p-xfppzqug-dv.html

    

四:Java 枚舉如何比較

  枚舉實例可使用 == 做比較,也能夠 使用 equals() 做比較,兩者結果同樣,由於Enum類重寫了 equals()方法,其實質仍是使用 == 做比較的。

public final boolean equals(Object other) {
    return this==other;
}

 

五:switch 對枚舉的支持

    來看一則 switch中用枚舉做case的代碼反編譯結果:

 package com.example.demo; import java.io.PrintStream; 
          ab public class EnumTest{ public EnumTest(){}
           public static transient void main(string args[])
           {ab a = ab.aaa;
           class _anm1 {} 
           
           switch(_cls1..SwitchMap.com.example.demo.ab[a.ordinal()]) //取的是枚舉的ordinal()方法的返回值
           {
               case 1: // '\001'system.out.println("aaa");
               // fall through 
              
               case 2: // '\002'system.out.println("bbb");
               // fall through
                default:return;
            
            }

}} 

  結合上文中第四點Enum類的解讀,ordinal()方法返回的是枚舉類中的ordinal成員變量值(一個int值),所以switch中用枚舉類型做比較時其實是用枚舉值的ordinal值做比較的。

  

六:枚舉的序列化與反序列化

  枚舉類型在序列化時僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是經過java.lang.Enum的valueOf方法來根據名字查找枚舉對象

  同時,編譯器禁止重寫枚舉類型的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

     

七:枚舉的線程安全性問題——爲何說枚舉類型是線程安全的

  從第二點,枚舉類反編譯獲得的代碼咱們能夠看到,枚舉類編譯出來的屬性都是static類型的,而static類型的屬性會在類被加載以後初始化,而Java類的加載和初始化過程都是線程安全的,因此建立一個enum類型是線程安全的。

相關文章
相關標籤/搜索