Java枚举类从入门到精通
1. 概述
枚举,是JDK1.5引入的新特性,可以通过关键字 enum 来定义枚举类。
枚举类意义
1)Java中的类,从语法上来说,可以创建无数个对象
例如, 学生类 Student,我们可以创建出10个、20个或更多数量的学生对象, 并且从实际业务上讲也没有问题,因为实际情况中确实会存在很多不同的学生。
2)Java特殊类,其所能创建的对象个数是固定的
例如, 性别类 Gender,表示人的性别,从语法上来说,可以创建出无数个性 别对象,但是从实际意义上看,我们只需要创建2个对象就可以了:性别男、性 别女。
3)如何实现上述Java特殊类
我们可以将 Gender 定义为一个 枚举类型(enum),在枚举类型中,我们需要 提前定义 枚举元素(枚举类型对象固定的几个取值),以后开发过程中只能使 用枚举元素给 Gerder类对象 赋值。
例如:
// 1. 定义枚举类
enum Gender {MALE, FEMALE
}public class Test071_EnumBasic {public static void main(String[] args) {// 2. 枚举类实例化对象// 固定格式:枚举类 引用名 = 枚举类.枚举元素值;Gender g1 = Gender.MALE;Gender g2 = Gender.MALE;System.out.println(g1); // 输出:MALESystem.out.println(g2); // 输出:MALESystem.out.println("-------------");Gender g3 = Gender.FEMALE;Gender g4 = Gender.FEMALE;System.out.println(g3); // 输出:FEMALESystem.out.println(g4); // 输出:FEMALE// 3. 错误用法:枚举对象的取值只能是定义好的枚举元素// Gender g5 = Gender.BOY; // 编译错误!BOY 不是 Gender 中的枚举常量}
}输出结果:MALE
MALE
-------------
FEMALE
FEMALE
上述案例中,我们定义了枚举类Gender ,有且只有俩个对象: MALE、FEMALE 。
4)枚举类分析
找到Gender类编译生成的字节码文件,对其反编译,观察枚举类定义细节,具体 如下:
Gender.class文件位置:
反编译命令: javap -p Gender.class
结合上图我们可知:
- 枚举类Gender本质上是一个final修饰的类,不可以被继承
- 枚举类会默认继承 java.lang.Enum 这个抽象泛型类
package java.lang;/*** 所有 Java 枚举类的公共基类。* 这个类不能被显式继承(由编译器控制),只能通过 enum 声明创建枚举类型。** @param <E> 此枚举类型的子类型*/
public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable {// 枚举常量的名称,例如:MALE、FEMALEprivate final String name;/*** 返回此枚举常量的名称。** @return 枚举常量的名称*/public final String name() {return name;}// 枚举常量的序号(ordinal),从 0 开始按声明顺序递增private final int ordinal;/*** 返回此枚举常量在其枚举类型中的位置(序号)。* 例如:MALE -> 0, FEMALE -> 1** @return 该枚举常量的序号*/public final int ordinal() {return ordinal;}// 省略其他方法:构造器、toString、equals、hashCode、compareTo、getDeclaringClass 等}
- 枚举元素,本质上是枚举类对象,且由 static和final修饰
- 枚举类提供私有构造器,我们在类外不能主动创建枚举类对象
- 枚举类中可以包含 public static 静态方法
- 其他细节我们暂不考虑
2. 枚举基本定义
定义格式:
[修饰符] enum 枚举类名 {
枚举元素1,枚举元素2,...枚举元素n;}
案例展示:
定义枚举类Week,要求包含多个枚举元素。
// 枚举类基本定义
enum Week {// 枚举常量必须写在第一行,多个之间用逗号分隔// 最后以分号结束(如果后面有字段或方法,则分号不可省略)// 推荐:即使没有后续内容,也保留分号,以保持一致性MON, TUE, WED;// 注意:// - WED 等价于 WED(),表示调用默认无参构造器创建实例// - 若未显式定义构造器,编译器会自动生成 private Week() {}
}
public class Test072_Define01 {public static void main(String[] args) {// 1. 枚举对象引用格式:枚举类名.枚举元素名// 注意:枚举类名不能省略Week w1 = Week.MON;Week w2 = Week.TUE;Week w3 = Week.WED;// 2. 输出枚举对象,默认调用 toString(),输出枚举元素名称System.out.println(w1); // 输出: MONSystem.out.println(w2.toString()); // 输出: TUESystem.out.println("-----------");// 3. 获取枚举元素名称(字符串形式)System.out.println(w3.name()); // 输出: WED// 4. 获取枚举元素的序号(ordinal):从 0 开始递增System.out.println(w1.ordinal()); // 输出: 0System.out.println(w2.ordinal()); // 输出: 1System.out.println(w3.ordinal()); // 输出: 2}
}
3. 构造方法的定义
包含数据成员、构造方法的枚举类
定义格式:
[修饰符] enum 枚举类名 {
枚举元素1(实际参数列表), ...枚举元素n(实际参数列表);//枚举类数据成员和成员方法,可以包含多个
[修饰符] 数据类型 数据成员名;[修饰符] 返回值类型 成员方法名(形参列表) {
方法体实现;}
//枚举类构造方法,可以包含多个
//注意,必须使用private进行修饰
private 构造方法;}
案例展示:
定义枚举类Week2,要求包含私有数据成员desc和构造方法。
// 包含数据成员和构造方法的枚举类
enum Week2 {// 枚举元素必须位于第一行有效代码// 每个元素会调用对应的构造方法(必须存在,否则编译错误)MON, // 调用 Week2() 无参构造器TUE(), // 显式调用 Week2() 无参构造器(等价于 TUE)WED("星期三"); // 调用 Week2(String desc) 有参构造器// 数据成员private String desc;// Getter 方法public String getDesc() {return desc;}// Setter 方法(一般不建议为枚举设置可变状态)public void setDesc(String desc) {this.desc = desc;}// 默认构造方法(private 可省略)private Week2() {this.desc = "未知"; // 可选:给默认值}// 带参数的构造方法private Week2(String desc) {this.desc = desc;}
}
public class Test073_Define02 {public static void main(String[] args) {Week2 w1 = Week2.MON;System.out.println(w1);System.out.println("------------");Week2 w2 = Week2.TUE;System.out.println(w2);System.out.println("w2.desc: " + w2.getDesc());System.out.println("------------");Week2 w3 = Week2.WED;System.out.println(w3);System.out.println("w3.desc: " + w3.getDesc());}
}
MON
------------
TUE
w2.desc: 未知
------------
WED
w3.desc: 星期三
4. 抽象方法定义
包含抽象方法的枚举类
定义格式:
[修饰符] enum 枚举类名 {
枚举元素1(实参列表) {
重写所有抽象方法;}, ...枚举元素n(实参列表) {
重写所有抽象方法;};//可以包含多个抽象方法
抽象方法声明;//数据成员、成员方法及构造方法略...}
案例展示:
定义枚举类Week3,要求包含抽象方法show()。
// 定义枚举类的第三种情况:包含抽象方法
enum Week3 {// 注意:// 1. 包含抽象方法的枚举类本质上是抽象的,不能被直接实例化。// 2. 每个枚举元素都必须以“类体”的形式存在,并重写所有抽象方法。// 3. 所有枚举元素必须重写全部抽象方法,否则编译报错。MON() {@Overridepublic void show() {System.out.println("in show, MON: 周一");}},TUE("星期二") {@Overridepublic void show() {System.out.println("in show, TUE: " + this.getDesc());}};// 数据成员private String desc;// Getter 方法public String getDesc() {return desc;}// 构造方法(无参)private Week3() {this.desc = "未知";}// 构造方法(带参)private Week3(String desc) {this.desc = desc;}// 抽象方法(可以有多个)public abstract void show();
}
public class Test074_Define03 {public static void main(String[] args) {Week3 w1 = Week3.MON;System.out.println(w1); // 输出:MONw1.show(); // 输出:in show, MON: 周一System.out.println("------------------");Week3 w2 = Week3.TUE;System.out.println(w2); // 输出:TUEw2.show(); // 输出:in show, TUE: 星期二}
}
5. 枚举总结
在实际项目开发中,我们定义枚举类型,大多数情况下使用最基本的定义方式, 偶尔会添加属性、方法和构造方法,并不会写的那么复杂。
枚举类注意事项: