实例有限并且固定的类,在Java里被称为枚举类。java
早期采用经过定义类的方式来实现,能够采用以下设计方式程序员
经过private将构造器隐藏起来this
把这个类的全部可能实例都使用public static final 修饰的类变量来保存设计
若是与必要,能够提供一些静态方法,容许其余程序根据特定参数来获取与之匹配的实例code
使用枚举类可使程序更加健壮,避免建立对象的随意性对象
Java从JDK1.5后就增长了对枚举类的支持。继承
Java5新增了一个enum关键字(它与class、interface关键字的地位相同),用以定义枚举类。枚举类是一种特殊的类,它同样能够有本身的成员变量、方法,能够实现一个或多个接口,也能够定义本身的构造器。一个Java源文件中最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。索引
枚举类能够实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,所以枚举类不能显示继承其余父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。接口
使用enum定义、非抽象的枚举类默认会使用final修饰,所以枚举类不能派生子类。get
枚举类的构造器只能使用private访问控制符,若是省略了构造器的访问控制符,则默认使用private修饰;若是强制指定访问控制符,则只能指定private修饰符。
枚举类的全部实例必须在枚举类的第一行显式列出,不然这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final 修饰,无须程序员显式添加。
枚举类默认提供了一个values()方法,该方法能够很方便地遍历全部的枚举值。
public enum SeasonEnum { //在第一行列出4个枚举实例 SPRING,SUMMER,FALL,WINTER; }
编译上面Java程序,将生成一个SeasonEnum.class文件,这代表枚举类是一个特殊的Java类。
全部的枚举值之间以英文逗号(,)隔开,枚举值列举结束后以英文分号做为结束。这些枚举值表明了该枚举类的全部可能的实例。
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(SeasonEnum s : SeasonEnum.values()) { System.out.println(s); } //使用枚举实例时,可经过EnumClass.variable形式来访问 new EnumTest().judge(SeasonEnum.SPRING); } }
当switch控制表达式使用枚举类型时,后面case表达式中的值直接使用枚举值的名字,无须添加枚举类做为限定。
java.lang.Enum类中提供了以下几个方法:
int compareTo(E o):该方法用于与指定枚举对象比较顺序,同一个枚举实例只能与相同类型的枚举实例进行比较。若是该枚举对象位于指定枚举对象以后,则返回正整数;若是该枚举对象位于指定枚举对象以前,则返回负整数,不然返回零。
String name():返回此枚举实例的名称,这个名称就是定义枚举类时列出的全部枚举值之一。与此方法相比,大多数程序员应该优先考虑使用toString()方法,由于toString()方法返回更加用户友好的名称。
int ordinal():返回枚举值在枚举类中的索引值(就是枚举值在枚举声明中的位置,第一个枚举值的索引值为零)。
String toString():返回枚举常量的名称,与name方法类似,但toString()方法更经常使用。
public static <T extends Enum <T>> T valueOf(Class<T>enumType, String name):这是一个静态方法,用于返回指定枚举类中指定名称的枚举值。名称必须与在该枚举类中声明枚举值时所用的标识符彻底匹配,不容许使用额外的空白字符。
当程序使用System.out.println(s)语句来打印枚举值时,实际上输出的是该枚举值的toString()方法,也就是输出该枚举值的名字。
枚举类的实例只能是枚举值,而不是随意地经过new来建立枚举类对象。
public enum Gender { MALE,FEMALE; //定义一个public修饰的实例变量 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) { //经过Enum的valueOf()方法来获取指定枚举类的枚举值 //Gender gender = Enum.valueOf(Gender.class, "FEMALE"); Gender g = Gender.valueOf("FEMALE"); g.setName("女"); System.out.println(g+"表明:"+g.getName()); g.setName("男"); System.out.println(g+"表明:"+g.getName()); } }
枚举类一般应该设计成不可变类,成员变量值不容许改变,将枚举类的成员变量都使用private final修饰。若是将全部的成员变量都使用final修饰符来修饰,必须在构造器里为这些成员变量指定初始值,为枚举类显式定义带参数的构造器。
一旦为枚举类显式定义了带参数的构造器,列出枚举值时就必须对应地传入参数。
public enum Gender { //此处的枚举值必须调用对应的构造器来建立 MALE("男"),FEMAL("女"); private final String name; private Gender(String name) { this.name =name; } public String getName() { return this.name(); } }
枚举类也能够实现一个或多个接口。与普通类实现一个或多个接口彻底同样,枚举类实现一个或多个接口时,也须要实现该接口所包含的方法。
public interface GenderDesc { void info(); }
若是由枚举类来实现接口里的方法,则每一个枚举值在调用该方法时都有相同的行为方式(由于方法体彻底同样)。若是须要每一个枚举值在调用该方法时呈现出不一样的行为方式,则可让每一个枚举值分别来实现该方法,每一个枚举值提供不一样的实现方式,从而让不一样的枚举值调用该方法时具备不一样的行为方式。
public enum Gender implements GenderDesc { // 此处的枚举值必须调用对应构造器来建立 MALE("男") // 花括号部分其实是一个类体部分 { public void info() { System.out.println("这个枚举值表明男性"); } }, FEMALE("女") { public void info() { System.out.println("这个枚举值表明女性"); } }; // 其余部分与codes\06\6.9\best\Gender.java中的Gender类彻底相同 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和FEMALE两个枚举值时,后面又紧跟了一对花括号,这对花括号里包含了一个info()方法定义。花括号部分实际上就是一个类体部分,在这种状况下,当建立MALE和FEMALE枚举值时,并非直接建立Gender枚举类的实例,而是至关于建立Gender的匿名子类的实例。
并非全部的枚举类都使用了final修饰。非抽象的枚举类才默认使用final修饰。对于一个抽象的枚举类而言——只要它包含了抽象方法,它就是抽象枚举类,系统会默认使用abstract修饰,而不是使用final修饰。
编译上面的程序,生成了Gender.class、Gender$1.class和Gender$2.class三个文件,证实了:MALE和FEMALE其实是Gender匿名子类的实例,而不是Gender类的实例。当调用MALE和FEMALE两个枚举值的方法时,就会看到两个枚举值的方法表现不一样的行为方式。
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(5, 4)); System.out.println(Operation.TIMES.eval(8, 8)); System.out.println(Operation.DIVIDE.eval(1, 5)); } }
枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义成抽象类(由于系统自动会为它添加abstract关键字),但由于枚举类须要显式建立枚举值,而不是做为父类,因此定义每一个枚举值时必须为抽象方法提供实现,不然将出现编译错误。