1. 程式人生 > >Java 列舉(enum)之淺進淺出

Java 列舉(enum)之淺進淺出

Java在1.5新增了列舉型別,說白了它就是一種特殊的Class;只是它不可以任意的去new例項物件,它的例項物件在定義enum時,就需要定義好了,這樣也就限制了這個類的範圍;比較常用的就是用來替代 public static final宣告的靜態常量。

一、定義列舉

(一)常量

使用IDEA的new可以直接建立一個enum出來;比如我們要定義動物園中一些動物常量,那就可以使用列舉來完成。

public enum Zoo {
    TIGER, PANDA, CAT, ELEPHANT, ALPACA
}

簡單的使用一下這些列舉:

public class TestEnum {
    public
static void main(String[] args) { // 可以直接輸出 System.out.println(Zoo.CAT); System.out.println(Zoo.PANDA); } }

(二)原理

這些常量就可以被打印出來了, 接下來我們通過反編譯來看一看Java是怎麼實現的列舉: 先編譯TestEnum.java:javac .\TestEnum.java 生成TestEnum.class和Zoo.class這兩個檔案; 然後反編譯Zoo.class:javap .\Zoo.class,生成如下程式碼:

public
final class Zoo extends java.lang.Enum<Zoo> { public static final Zoo TIGER; public static final Zoo PANDA; public static final Zoo CAT; public static final Zoo ELEPHANT; public static final Zoo ALPACA; public static Zoo[] values(); public static Zoo valueOf(java.lang.String); static
{}; }

通過反編譯Java幫我們生成了一個繼承 java.lang.Enum的final型的Class,以及之前定義的例項物件,通過程式碼可以看出來我們定義的列舉物件,實際上就是public static final靜態常量;其實Java還會強制把列舉的構造方法預設成私有的,綜上,列舉實際上就是一個特殊的常量類,這個類不可以被使用者去new新的例項。

(三)成員變數、自定義方法

我們在使用靜態常量的時候往往需要使用其數值,其實列舉例項也可以有屬於它自己的值,我們可以像定義普通類一樣去給列舉加上成員變數和方法,但要注意的是它的構造方法必須是私有的,以防止被外部呼叫。

public enum Zoo {
	// 注意:例項後的 ‘;' 如果不加這個;號,編譯器會報錯;
	// 同時也要保證例項定義在列舉的最上方,否則也會報錯。
    TIGER(1, "凶猛"), PANDA(2, "國寶"), CAT(3, "可愛"),
     ELEPHANT(4, "龐大"), ALPACA(5, "神獸");
    private int num;
    private String desc;

    private Zoo(int num, String desc) {
        this.num = num;
        this.desc = desc;
    }

	// 普通方法
    public static String getDesc(int num) {
        // 列舉的values方法,可以返回一個列舉例項陣列
        Zoo[] values = Zoo.values();
        for (Zoo value : values) {
            if(value.getNum() == num) {
                return value.getDesc();
            }
        }
        return null;
    }
    
    // 列舉中也可以重寫父類方法
    @Override
    public String toString() {
        return "Zoo{" +
                "num=" + num +
                ", desc='" + desc + '\'' +
                '}';
    }
    
    // get&&set方法
    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
// 測試
public class TestEnum {
    public static void main(String[] args) {
        // 可以直接輸出
        System.out.println(Zoo.CAT);
        System.out.println(Zoo.PANDA);

        int num = Zoo.PANDA.getNum();
        System.out.println(num);
        // 利用序號獲得PANDA的描述
        System.out.println(Zoo.getDesc(num));
    }
}

輸出結果:

Zoo{num=3, desc='可愛'}
Zoo{num=2, desc='國寶'}
2
國寶

列舉也支援定義抽象方法,這時的列舉類似一個抽象類,它的每一個列舉例項都需要實現該方法,這樣不同的例項就有不同的行為方式。

public enum Zoo2 {
    TIGER{
      @Override
      public  String eat(){
            return "吃肉";
      }
    },
    PANDA{
        @Override
        public  String eat(){
            return "吃竹子";
        }
    };

    // 定義抽象方法
    public abstract String eat();

    // 測試
    public static void main(String[] args) {
        System.out.println(Zoo2.TIGER + Zoo2.TIGER.eat());
        System.out.println(Zoo2.PANDA + Zoo2.PANDA.eat());
    }
}

輸出結果:

TIGER吃肉
PANDA吃竹子

(四)列舉實現介面

由於所有的列舉都會繼承java.lang.Enum類且Java遵循單繼承規則,所以列舉不會再繼承其它的類,但是列舉可以實現介面。

// 定義一個介面
public interface Food {
    void eat();
}
public enum Zoo3 implements Food{
    TIGER, PANDA, CAT;

    @Override
    public void eat() {
        System.out.println(this + "吃飼料");
    }

    public static void main(String[] args) {
        Zoo3.TIGER.eat();
        Zoo3.CAT.eat();
    }
}

輸出結果:

TIGER吃飼料
CAT吃飼料

列舉除了可以實現介面以外,還可以利用介面把相似型別的列舉統一的組織起來,定義在同一個介面中。

public interface Zoo4 {
    enum Carnivore implements Zoo4 {
        TIGER, LION, CAT
    }
    enum Herbivore implements Zoo4 {
        ELEPHANT, ALPACA
    }
}

這樣寫出來的程式碼更易讀;列舉也可以巢狀列舉,這樣也易於管理這些列舉:

public enum Animals {
    FEROCITY(Zoo4.Carnivore.class),
    LOVELINESS(Zoo4.Herbivore.class);

    private Zoo4[] values;
	// 把類的例項物件放進列舉裡
    private Animals(Class<? extends Zoo4> animal) {
        values = animal.getEnumConstants();
    }

    public interface Zoo4 {
        enum Carnivore implements Zoo4 {
            TIGER, LION, CAT
        }

        enum Herbivore implements Zoo4 {
            ELEPHANT, ALPACA
        }
    }
}

(五)列舉與switch

從JDK 1.7開始,switch變得豐富了,接下來我們看看它和列舉的一起使用。

public enum Zoo {
    TIGER, PANDA, CAT, ELEPHANT, ALPACA
}
public class EnumSwitch {
    public static void main(String[] args) {
        print(Zoo.PANDA);
    }

    public static void print(Zoo zoo) {
        switch (zoo) {
            case TIGER:
                System.out.println("老虎");
                break;
            case PANDA:
                System.out.println("熊貓");
                break;
            case ELEPHANT:
                System.out.println("大象");
                break;
        }
    }
}

二、Enum的常用方法

public class TestEnum {
    public static void main(String[] args) {
        // 1. compareTo()方法比較此列舉與指定物件的順序
        int compareTo = Zoo.PANDA.compareTo(Zoo.CAT);
        System.out.println(compareTo);

        // 2. equals()方法判斷指定物件是否等於此列舉常量
        boolean equals = Zoo.TIGER.equals(Zoo.PANDA);
        System.out.println(equals);

        // 3. getDeclaringClass()方法返回此列舉常量的列舉型別相對應的Class物件
        Class<Zoo> aClass = Zoo.PANDA.getDeclaringClass();
        System.out.println(aClass);

        // 4. ordinal()方法返回列舉物件在列舉中的序號(從0開始)
        int ordinal = Zoo.TIGER.ordinal();
        System.out.println(ordinal);

        // 5. name()方法返回列舉常量的名稱
        String name = Zoo.PANDA.name();
        System.out.println(name);
		
		 // 6. values()方法返回列舉例項物件陣列
        Zoo[] values = Zoo.values();

        // 7. valueOf()方法返回一個指定列舉常量
        Zoo panda = Zoo.valueOf("PANDA");
        System.out.println(panda);
    }
}

輸出結果:

-1
false
class Zoo
0
PANDA
[LZoo;@1b6d3586
Zoo{num=2, desc='國寶'}