在一些情况下,一个类的对象是有限且固定的,如季节类,只有春夏秋冬4个对象,这种实例有限且固定的类,在JAVA里称为枚举类。
1.手动实现枚举类
采用的设计方式:
通过private将构造器隐藏起来
把这个类所有可能的实例都使用public static final修饰的类变量来保存
首先定义一个Season类
public class Season{
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 String getName() {
return name;
}
public String getDesc() {
return desc;
}
public Season(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
}
public class SeasonTest {
public SeasonTest(Season s){
System.out.println(s.getName()+" "+s.getDesc());
}
public static void main(String[] args){
new SeasonTest(Season.FALL);
}
}
从上面程序不难看出,使用枚举类可以使程序更加健壮,避免对象的随意性,在更早前,程序员还喜欢使用简单的静态常量来表示:
如 public static final int SEASON_PRING=1;
虽然简单明了,但存在几点问题:
类型不安全,因为是int类型,当当成整数计算时也不会出错
没有命名空间,容易和其他变量混淆
打印输出不明确,输出1很难联想到春天
2.为了更好处理实例固定的类,JAVA5新增了Enum关键字(它与class、interface地位相同),用于定义枚举类。
枚举类与普通类的区别:
使用enum定义的非抽象的枚举类默认使用final修饰,因此不能派生子类
构造器只能用private修饰,默认也是private
枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例,列出的实例默认会使用public static final修饰
格式如下:
public enum Color{
YELLOW,BLUE,GREEN;
}
编译上面的程序,将生成一个Color.class文件,表面Enum是一个特殊的JAVA文件,枚举值以逗号隔开,分号结束
测试:
public class Test{
public static void main(String[] args) {
//所有枚举类都有一个values方法,返回该枚举类的所有实例
for(Color c : Color.values()){
switch(c){
case YELLOW: System.out.println("yellow");break;
case BLUE: System.out.println("blue"); break;
case GREEN: System.out.println("green"); break;
}
}
}
}
枚举类其他常用方法:
int compareTo(E o) ---------- 比较此枚举与指定对象的顺序
String toString() ---------- 返回枚举常量的名称
int ordinal() ---------- 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零
(其余方法详见API)
3.枚举类的Field、方法、构造器
枚举类也可以定义Filed,方法,如:
public enum Gender {
MALE,FEMALE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class GenderTest {
public static void main(String[] args) {
//通过Enum的valueOf方法来或取指定枚举类的枚举值
Gender g = Enum.valueOf(Gender.class,"FEMALE");
//直接为枚举值的Field赋值
g.setName("女");
//输出
System.out.println(g+","+g.getName());
}
}
输出:FEMALE,女
需要注意一点,枚举类的实例只能是枚举值,而不是随意通过new来创建枚举类对象。
实际上枚举类通常被设计为不可变类,它的Field不应该允许改变,对Filed最好使用private final修饰,如:
public enum Gender {
MALE("男"),FEMALE("女");
private final String name;
private Gender(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class GenderTest {
public static void main(String[] args) {
//直接输出
System.out.println(Gender.MALE+","+Gender.MALE.getName());
//通过Enum的valueOf方法来或取指定枚举类的枚举值
Gender g = Enum.valueOf(Gender.class,"FEMALE");
//输出
System.out.println(g+","+g.getName());
}
}
输出:MALE,男
FEMALE,女
4.实现接口的枚举类
枚举类也可以像其他类一样实现接口,如:
public interface GenderDesc {
void info();
}
public enum Gender implements GenderDesc{
//...与上面相同
@Override
public void info() {
System.out.println("这是一个用于定义性别的枚举类");
}
}
虽然实现了接口,但每个枚举值在调用该方法时都实现了相同的行为方式,如果需要不同的行为方式,可以使用下面做法:
public enum Gender implements GenderDesc{
MALE("男"){
public void info(){
System.out.println("此枚举值代表男性");
}
},
FEMALE("女"){
public void info(){
System.out.println("此枚举值代表女性");
}
};
private final String name;
private Gender(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void info() {
System.out.println("这是一个用于定义性别的枚举类");
}
}
public class GenderTest {
public static void main(String[] args) {
//输出
Gender.FEMALE.info();
Gender.MALE.info();
}
}
输出: 此枚举值代表女性
此枚举值代表男性
从输出可以看出,优先调用自身实现的行为方式,没有时枚举值才会调用公共方式。
上面的花括号其实是一个匿名内部类的类体部分,在使用花括号情况下,当创建MALE,FEMALE枚举值时,并不是直接创建Gender枚举类的实例,而是相当于创建Gender的匿名子类的实例,因为通过编译上面的程序,可以看出生成Gender.class,Gender$1.class和Gender$2.class三个文件,所以当我们调用MALE,FEMALE的方法时,会表现不同的行为方式。
5.包含抽象方法的枚举类
先看一个例子:
public enum Operation {
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(3, 4));
System.out.println(Operation.TIMES.eval(3, 4));
System.out.println(Operation.DIVIDE.eval(3, 4));
}
}
输出:7.0 -1.0 12.0 0.75
在接口那我们提到,在使用花括号情况下创建的是匿名子类的实例,当枚举类定义抽象方法时默认会添加abstract关键字,所以需要在枚举值花括号中进行方法的实现,否则会出现编译错误。