Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。
enum的特点
- 在 Java 中使用 enum 关键字来定义枚举类,其地位与 class、interface 相同;
- 枚举类和普通的类一样,可以有自己的成员变量、成员方法、构造器 (只能使用 private 访问修饰符,所以无法从外部调用构造器,构造器只在构造枚举值时被调用);
- 使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口;
- 枚举类的所有枚举值必须在枚举类的第一行显式地列出。系统会自动添加 public static final 修饰,无需显式添加;
枚举值是一个定义好的枚举类的实例. - 非抽象的枚举类不能再派生子类。
1. 基本用法:
Java 枚举类使用 enum 关键字来定义,各个常量使用逗号,来分割。 ```java enum Color { RED, GREEN, BLUE; }
public class MyClass { public static void main(String[] args) { Color myVar = Color.BLUE;
switch(myVar) {case RED:System.out.println("红色");break;case GREEN:System.out.println("绿色");break;case BLUE:System.out.println("蓝色");break;}
} }
<a name="PX863"></a>### 2. 高级用法:枚举跟普通类一样可以用自己的变量、方法和构造函数,**构造函数只能使用 private 访问修饰符**,所以外部无法调用。<a name="LCdlh"></a>#### A. 枚举类的成员变量、成员方法、构造器```javaenum Color{RED("红色"), GREEN("绿色"), BLUE("蓝色");//成员属性private String color;// 构造函数private Color(){System.out.println("Constructor called for : " + this.toString());}// 成员方法public void colorInfo(){System.out.println("Universal Color");}//getter and setterpublic String getColor(){return color;}public void setColor(String color){this.color = color;}}public class Test{// 输出public static void main(String[] args){Color c1 = Color.RED;System.out.println(c1); //结果为 "RED"c1.colorInfo(); //结果为 "Universal Color"System.out.println(c1.getColor()); //结果为"红色"}}
B. 枚举类中的常用方法
- int ordinal(): 返回枚举值在枚举类中的索引值(从0开始),即枚举值在枚举声明中的顺序,这个顺序根据枚举值声明的顺序而定;
- int compareTo(E o) and boolean equals(E o): 根据索引值进行比较;
- static values(): 返回一个包含全部枚举值的数组,可以用来遍历所有枚举值;
- String name(): 返回即枚举值的名称 ;
String toString(): 返回枚举值的名称,与 name 方法类似,更常用;
// 定义一个星期的枚举类public enum WeekEnum {// 因为已经定义了带参数的构造器,所以在列出枚举值时必须传入对应的参数SUNDAY("星期日"), MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"),THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星期六");// 定义一个 private 修饰的实例变量private String date;// 定义一个带参数的构造器,枚举类的构造器只能使用 private 修饰private WeekEnum(String date) {this.date = date;}// 定义 get set 方法public String getDate() {return date;}public void setDate(String date) {this.date = date;}// 重写 toString() 方法@Overridepublic String toString(){return date;}}
```java for (WeekEnum we : WeekEnum.values()) { System.out.print(we.name() +”, “); } 运行结果: SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY,
// 重写了 toString 方法 for (WeekEnum we : WeekEnum.values()) { System.out.print(we + “, “); } 运行结果: 星期日, 星期一, 星期二, 星期三, 星期四, 星期五, 星期六,
<a name="X9bue"></a>#### C. 枚举类实现接口与普通类一样,枚举类也可以实现一个或多个接口。枚举类实现接口时,同样要实现<br />该接口的所有方法。```javainterface GenderDescription {void info();}enum Gender implements GenderDescription {MALE, FEMALE;@Overridepublic void info() {System.out.println("这是一个用于定义性别的枚举类");}}public class Test02 {public static void main(String[] args) {Gender.MALE.info();Gender.FEMALE.info();}}
D. 包含抽象方法的枚举类
枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。
定义一个 Operation 枚举类,有4个枚举值PLUS、MINUS、TIMES、DIVIDE,分别代表加、减、乘、除,该枚举类有一个 calculate() 方法,用于完成计算。
public enum Operation {// 用于执行加法运算PLUS { // 花括号部分其实是一个匿名内部子类@Overridepublic double calculate(double x, double y) {return x + y;}},// 用于执行减法运算MINUS { // 花括号部分其实是一个匿名内部子类@Overridepublic double calculate(double x, double y) {// TODO Auto-generated method stubreturn x - y;}},// 用于执行乘法运算TIMES { // 花括号部分其实是一个匿名内部子类@Overridepublic double calculate(double x, double y) {return x * y;}},// 用于执行除法运算DIVIDE { // 花括号部分其实是一个匿名内部子类@Overridepublic double calculate(double x, double y) {return x / y;}};//为该枚举类定义一个抽象方法,枚举类中所有的枚举值都必须实现这个方法public abstract double calculate(double x, double y);}public class Test03 {public static void main(String[] args) {System.out.println("6 + 2 = " + Operation.PLUS.calculate(6, 3));System.out.println("6 - 2 = " + Operation.MINUS.calculate(6, 2));System.out.println("6 * 2 = " + Operation.TIMES.calculate(6, 2));System.out.println("6 / 2 = " + Operation.DIVIDE.calculate(6, 2));}}
3. 使用enum写SIngleton:
public enum SingleTon {INSTANCE;public void method() {System.out.println("我很牛逼!");}}
不比不知道,一比吓一跳啊!枚举方式的单例简单到爆——为了不至于看起来太过精简,我还加了一个输出“我很快乐”的方法。
枚举实现的单例可轻松地解决两个问题:
①、线程安全问题。因为Java虚拟机在加载枚举类的时候,会使用ClassLoader的loadClass方法,这个方法使用了同步代码块来保证线程安全。
②、避免反序列化破坏单例。因为枚举的反序列化并不通过反射实现。
