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='國寶'}