Java枚舉的用法和原理深入


 

轉載請注明原文地址:https://www.cnblogs.com/ygj0930/p/10843644.html

 

一:枚舉的用法

  1、定義和組織常量   

  在JDK1.5之前,我們定義常量都是:public static fianl....。有了枚舉之后,我們可以把相關的常量定義到一個枚舉類里,而且枚舉類也提供了比常量更多的操作方法來操縱。

  用法舉例:

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

    看不懂沒關系,上面的枚舉類定義原理在下文中再解釋。

   

  2、用於switch

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

  用法舉例:

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

 

  3、向枚舉中自定義屬性和方法

  我們在定義枚舉時,其實是在定義一個Enum類的子類,我們可以往其中添加自定義的屬性和方法。

  但是要注意:我們需要在枚舉類中先定義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;  
    }  
}  

 

  4、重寫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;  
    }  
}  

 

  5、實現接口

  前面提到,用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);  
    }  
}  

 

  6、EnumSet的使用

  java.util.EnumSet是枚舉類型的集合類,可以保證集合中的枚舉元素不重復。

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

 

  7、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) 
          返回帶指定名稱的指定枚舉類型的枚舉常量。

 

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

  參考我的另一片博文;https://www.cnblogs.com/ygj0930/p/10845530.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類型是線程安全的。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM