在某些情況,一個類的對象是有限而且是固定的,例如季節類,只有春夏秋冬四個對象,這種實例有限且固定的類,成爲枚舉類
枚舉類概念和作用
在JAVA5之前的枚舉用法是這樣的:
public static final int SEASON_SPRING = 1;
public static final int SEASON_SUMMER = 2;
public static final int SEASON_FALL = 3;
public static final int SEASON_WINTER = 4;
public class Season
{
// 把Season類定義成不可變的,將其成員變量也定義成final的
private final String name;
private final String desc;
public static final Season SPRING
= new Season("春天", "趁春踏青");
public static final Season SUMMER
= new Season("夏天", "夏日炎炎");
public static final Season FALL
= new Season("秋天", "秋高氣爽");
public static final Season WINTER
= new Season("冬天", "圍爐賞雪");
public static Season getSeason(int seasonNum)
{
switch (seasonNum)
{
case 1 :
return SPRING;
case 2 :
return SUMMER;
case 3 :
return FALL;
case 4 :
return WINTER;
default :
return null;
}
}
// 將構造器定義成private訪問權限
private Season(String name, String desc)
{
this.name = name;
this.desc = desc;
}
// 只爲name和desc提供getter方法
public String getName()
{
return this.name;
}
public String getDesc()
{
return this.desc;
}
}
在JAVA5之後新增了enum關鍵字,它與class,interface關鍵字的地位相同,用於定義枚舉類,枚舉類一樣可以有自己的成員變量、方法,可以實現一個或多個接口,也可以定義自己的構造器
一個Java源文件只能有一個public訪問權限的枚舉類,並且這個Java源文件的文件名必須與該枚舉類的類名相同
枚舉類與普通類的不同
- 枚舉類可以實現一個或多個接口,使用enum定義的枚舉類默認繼承了java.lang.Enum類而不是Object類,枚舉類不能顯示的繼承其父類,java.lang.Enum實現了java.lang.Serializable和java.lang.Comparable接口
- 使用enum定義的非抽象的枚舉類默認會使用final修飾
- 枚舉類的構造器只能使用private訪問控制符,如果省略了則系統默認會加上private修飾,因此枚舉類不能派生子類
- 枚舉類所有實例必須在枚舉類的第一行顯示的列出,否則這個枚舉類永遠不能產生實例,列出這些實例,系統會自動添加public static final修飾,無需顯示添加
public enum SeasonEnum
{ // 顯示的列出4個枚舉類
SPRING, SUMMER, FALL, WINTER;
}
顯示的使用枚舉類的實例EnumClass.variable,例如SeasonEnum.SPRING
public class EnumTest
{
public void judge(SeasonEnum s)
{
// switch語句裏的表達式可以是枚舉值
switch (s)
{
case SPRING:
System.out.println("春暖花開,正好踏青");
break;
case SUMMER:
System.out.println("夏日炎炎,適合游泳");
break;
case FALL:
System.out.println("秋高氣爽,進補及時");
break;
case WINTER:
System.out.println("冬日雪飄,圍爐賞雪");
break;
}
}
public static void main(String[] args)
{
// 枚舉類默認有一個values方法,返回該枚舉類的所有實例
for (var s : SeasonEnum.values())
{
System.out.println(s);
}
// 使用枚舉實例時,可通過EnumClass.variable形式來訪問
new EnumTest().judge(SeasonEnum.SPRING);
}
}
switch表達式使用了SeasonEnum對象作爲表達式(JDK1.5增加了枚舉後對switch進行了擴展),switch的控制表達式可以是任何枚舉類型,並且case表達式中的值直接使用枚舉值的名字,無需添加枚舉類作爲限定
java.lang.Enum類中的幾個方法
- int compareTo(E o):
- String name():
- int ordinal():
- String toString():
- public static <T extends Enum>T valueOf(ClassenumType, String name):
枚舉類的成員變量、方法和構造器
public enum Gender
{
MALE, FEMALE;
public String name;
}
public class GenderTest
{
public static void main(String[] args)
{
// 通過Enum的valueOf()方法來獲取指定枚舉類的枚舉值
Gender g = Enum.valueOf(Gender.class, "FEMALE");
// 直接爲枚舉值的name實例變量賦值
g.name = "女";
// 直接訪問枚舉值的name實例變量
System.out.println(g + "代表:" + g.name);
}
}
枚舉類用起來根普通類區別不大,只是創建對象的方式不同,枚舉類實例只能是枚舉值而不是通過new來創建枚舉類對象
然而爲了更好的封裝性,不應該允許直接訪問到類的成員變量,而是用方法來控制,枚舉類也不例外
public enum Gender
{
MALE, FEMALE;
private String name;
public void setName(String name)
{
switch (this)
{
case MALE:
if (name.equals("男"))
{
this.name = name;
}
else
{
System.out.println("參數錯誤");
return;
}
break;
case FEMALE:
if (name.equals("女"))
{
this.name = name;
}
else
{
System.out.println("參數錯誤");
return;
}
break;
}
}
public String getName()
{
return this.name;
}
}
public class GenderTest
{
public static void main(String[] args)
{
Gender g = Gender.valueOf("FEMALE");
g.setName("女");
System.out.println(g + "代表:" + g.getName());
// 此時設置name值時將會提示參數錯誤。
g.setName("男");
System.out.println(g + "代表:" + g.getName());
}
}
枚舉類通常設計成不可變類,他的成員變量值不應該允許改變,因此建議使用private final修飾,同時如果所有的成員變量都使用了private final修飾,則必須在構造器爲這些成員變量指定初始值(或者在定義成員變量時指定默認值,或者在初始化塊中指定初始值)
public enum Gender
{
// 此處的枚舉值必須調用對應構造器來創建
MALE("男"), FEMALE("女");
private final String name;
// 枚舉類的構造器只能使用private修飾
private Gender(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
當爲枚舉類創建了一個Gender(String name)構造器後,列出的枚舉值就應該如代碼所示來完成,實際上枚舉類中列出枚舉值時,就是調用構造器創建枚舉對象,只是不需要new,不需要顯示調用構造器
實現接口的枚舉類
枚舉類可以實現一個或者多個接口,與普通類實現一個或多個接口一樣,枚舉類實現接口時,也需要實現該接口所包含的方法
public interface GenderDesc
{
void info();
}
public enum Gender implements GenderDesc
{
// 此處的枚舉值必須調用對應構造器來創建
MALE("男"), FEMALE("女");
// 此處的枚舉值必須調用對應構造器來創建
private final String name;
// 枚舉類的構造器只能使用private修飾
private Gender(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// 增加下面的info()方法,實現GenderDesc接口必須實現的方法
public void info()
{
System.out.println(
"這是一個用於用於定義性別的枚舉類");
}
}
如果要每個枚舉值在調用該方法時呈現出不同的行爲則需要讓每個枚舉值分別來實現該方法
MALE("男")
// 花括號部分實際上是一個類體部分
// 枚舉類的匿名內部子類
{
public void info()
{
System.out.println("這個枚舉值代表男性");
}
},
FEMALE("女")
{
public void info()
{
System.out.println("這個枚舉值代表女性");
}
};
編譯時會生成三個文件Gender.class/Gender$1.class/Gender$2.class
包含抽象方法的枚舉類
並不是所有的枚舉類都使用final修飾,非抽象的枚舉類才默認使用final修飾,對於一個抽象枚舉類來說,只要它包含了抽象抽象方法,它就是個抽象枚舉類,系統默認使用abstract修飾
public enum Operation
{
// 定義枚舉值時爲抽象方法提供實現,否則報錯
// 枚舉類裏定義抽象方法,不需要把枚舉類也用abstract修飾,系統會自動加上
PLUS
{
public double eval(double x, double y)
{
return x + y;
}
},
MINUS
{
public double eval(double x, double y)
{
return x - y;
}
},
TIMES
{
public double eval(double x, double y)
{
return x * y;
}
},
DIVIDE
{
public double eval(double x, double y)
{
return x / y;
}
};
// 爲枚舉類定義一個抽象方法
// 這個抽象方法由不同的枚舉值提供不同的實現
public abstract double eval(double x, double y);
public static void main(String[] args)
{
System.out.println(Operation.PLUS.eval(3, 4));
System.out.println(Operation.MINUS.eval(5, 4));
System.out.println(Operation.TIMES.eval(5, 4));
System.out.println(Operation.DIVIDE.eval(5, 4));
}
}