12. 枚举
有些特殊类在编写时,我们明确知道他的对象存在,更愿意在创建类时就将这些对象实现,也不允许此类被其他类继承,同时让这些特定的对象唯一。
如:季节类(Season),该类只有四个对象(spring,summer,autumn,winter)
交通信号灯类(TrafficLights),该类只有三个对象(red,yellow,green)
在 JDK1.5 开始,引入枚举(enum)全称为 enumeration。
12.1 语法规则
public enum Season{
//首行必须定义对象,这些对象默认添加 public static final Season
spring,summer,autumn,winter;
//其后添加属性和方法。
}
注:1.首行必须定义对象,对象之间使用“,”(英文逗号)分隔开。
2.对象定义结束使用“;”(英文分号)结尾。
3.若不想先定义对象,可以直接使用;隔开,在其后写属性和方法。
4.枚举类的构造方法为private,表示私有,不可被外部调用,即是:外部不能创建对象。
5.枚举类默认的添加final,表示不可被继承,但可以实现接口。
6.枚举中的方法可以有:构造、成员、静态、抽象。
7.枚举里没有定义方法,可以在最后一个对象后面加逗号、分号或什么都不加。
12.2 枚举的本质
尽管枚举看起来像是一种新的数据类型,但实际上,枚举是一种受限制的类,并且具有自己的方法。
在创建自己的enum类时,这个类继承自 java.lang.Enum。
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable{
...
}
12.3 枚举的优势
增强代码可读性
枚举型可直接与数据库交互
switch语句优势
编译优势
(枚举类编译时,没有把常量值编译到代码中,即使常量值发生改变,也不会影响引用常量的类 )
将常量组织起来,统一管理
去除equals两者判断 由于常量值地址唯一,使用枚举可以直接通过“==”进行两个值之间的对比,性能会有所提高
12.4 枚举的方法
方法名 | 解释 |
---|---|
Enum.valueOf(Class enumType, String name) | 根据字符串找到该枚举类中的对象 |
public static void values() | 获取该枚举类对象数组 |
public static void valueOf(String args0) | 根据字符串获取该枚举类中的对象 |
public final String name() | 获取该枚举对象名字 |
public final Class getDeclaringClass() | 获取枚举对象的枚举类型相对应的Class对象 |
public final int hashCode() | 获取该枚举对象的hash值 |
public final int compareTo(E o) | 两个枚举对象进行比较 |
public final boolean equals(Object other) | 比较两个枚举对象是否相同 |
使用示例:
public enum Season { spring("春天","春雨蒙蒙"), summer("夏天","大汗淋漓"), autumn("秋天","硕果累累"), winter("冬天","银装素裹"); private String name; private String info; private Season(String name, String info) { this.name = name; this.info = info; } private Season() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } @Override public String toString() { return name + " -- " + info; } }
测试方法:
public static void main(String[] args) { //根据字符串找到该枚举类中的对象 Season season1 = Enum.valueOf(Season.class, "spring"); System.out.println(season1); //春天 -- 春雨蒙蒙 //获取该枚举类对象数组 Season[] values = Season.values(); for (Season season : values) { System.out.println(season); } //根据字符串获取该枚举类中的对象 Season valueOf = Season.valueOf("autumn"); System.out.println(valueOf); //秋天 -- 硕果累累 //根据对象获取出对象名 String name = valueOf.name(); System.out.println(name); //autumn //获取枚举对象的枚举类型相对应的Class对象 Class<Season> declaringClass = valueOf.getDeclaringClass(); System.out.println(declaringClass); class com.dream.test04.Season }
12.5 枚举案例
12.5.1 状态机(switch优势)
import java.util.Scanner;
public class Test_4 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入信号灯:RED,YELLOW,GREEN");
//通过输入的枚举对象名获取对应对象。
Signal signal = Signal.valueOf(scan.next());
String intruct = getTrafficInstruct(signal);
System.out.println(intruct);
}
public static String getTrafficInstruct(Signal signal){
String intruct ="信号灯故障";
switch(signal){
case RED:
intruct="当前红灯,停";
break;
case YELLOW:
intruct="当前黄灯,注意安全";
break;
case GREEN:
intruct="当前绿灯,行";
break;
default :
break;
}
return intruct;
}
}
//定义枚举类Signal
enum Signal{RED,YELLOW,GREEN}
12.5.2 错误码
public enum ErrorCodeEn {
Ok(1,"成功"),ERROR_A(2,"错误A"),ERROR_B(3,"错误B");
private int code;//状态码
private String description;//状态信息
ErrorCodeEn(){}
ErrorCodeEn(int code,String description){
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
}
12.5.3 组织枚举
含义:可以将类型相近的枚举通过接口或类组织起来(但是一般用接口方式进行组织)
原因是:
Java接口在编译时会自动为enum类型加上public static修饰符;
Java类在编译时会自动为 enum 类型加上static修饰符;
就是说,在类中组织 enum,如果你不给它修饰为 public,那么只能在本包中进行访问。
public interface IErrorCode {
enum LoginErrorCodeEn implements INumberEnum{
OK(1,"登录成功"),ERROR_A(-1,"验证码错误"),ERROR_B(-2,"密码错误"),ERROR_C(-3,"用户已登录");
private int code;
private String description;
LoginErrorCodeEn(int code,String description){
this.code = code;
this.description = description;
}
@Override
public int getCode() {
return code;
}
@Override
public String getDescription() {
return description;
}
}
enum RigsterErrorCodeEn implements INumberEnum{
OK(1,"注册成功"),ERROR_A(-1,"账号已存在");
private int code;
private String description;
RigsterErrorCodeEn(int code,String description){
this.code = code;
this.description = description;
}
@Override
public int getCode() {
return code;
}
@Override
public String getDescription() {
return description;
}
}
}
interface INumberEnum {
int getCode();
String getDescription();
}
12.5.4 策略枚举
优点:这种枚举通过枚举嵌套枚举的方式,将枚举常量分类处理。
这种做法虽然没有switch语句简洁,但是更加安全、灵活。
import java.math.BigDecimal;
public class Test_5 {
public static void main(String[] args) {
double salary_java = Salary.java.getSalary(1000, 20, 80, 500);
System.out.println("java教师的工资:"+salary_java);
double salary_python = Salary.python.getSalary(1200, 25, 80, 600);
System.out.println("Python教师的工资:"+salary_python);
double salary_principal = Salary.principal.getSalary(5000, 0, 0, 5000);
System.out.println("校长的工资为:"+salary_principal);
double salary_reception = Salary.reception.getSalary(1500, 0, 0, 500);
System.out.println("前台的工资为:"+salary_reception);
}
}
enum Salary{
java(StaffType.teacher),//java部门
python(StaffType.teacher),//python部门
principal(StaffType.administrative),//总经办(校长)部门
reception(StaffType.administrative);//前台部门
private StaffType staffType;
private Salary(StaffType staffType) {
this.staffType = staffType;
}
/**
* 计算月工资
* @param baseSalary 基本工资
* @param classHour 课时
* @param teachingHourSubsidy 课时费
* @param achievements 绩效
* @return 工资(double)
*/
public double getSalary(double baseSalary, int classHour, double teachingHourSubsidy,
double achievements){
return staffType.calculationSalary(baseSalary, classHour, teachingHourSubsidy, achievements);
}
enum StaffType{
//创建匿名内部类
administrative{
@Override
public double calculationSalary(double baseSalary, int classHour, double teachingHourSubsidy,
double achievements) {
BigDecimal bigBaseSalary = new BigDecimal(String.valueOf(baseSalary));
BigDecimal bigAchievements = new BigDecimal(String.valueOf(achievements));
BigDecimal add = bigBaseSalary.add(bigAchievements);
return add.doubleValue();
}
},
//创建匿名内部类
teacher{
@Override
public double calculationSalary(double baseSalary, int classHour, double teachingHourSubsidy,
double achievements) {
BigDecimal bigBaseSalary = new BigDecimal(String.valueOf(baseSalary));
BigDecimal bigClassHour = new BigDecimal(String.valueOf(classHour));
BigDecimal bigTeachingHourSubsidy = new BigDecimal(String.valueOf(teachingHourSubsidy));
BigDecimal bigAchievements = new BigDecimal(String.valueOf(achievements));
BigDecimal add = bigBaseSalary.add(bigAchievements).add(bigClassHour.multiply(bigTeachingHourSubsidy));
return add.doubleValue();
}
};
//枚举中的抽象方法,留给匿名内部类实现。
public abstract double calculationSalary(double baseSalary,int classHour,double teachingHourSubsidy,double achievements);
}
}
/*
java教师的工资:3100.0
Python教师的工资:3800.0
校长的工资为:10000.0
前台的工资为:2000.0
*/
12.6 枚举工具类 - EnumSet 和 EnumMap
Java 中提供了两个方便操作enum的工具类——EnumSet 和 EnumMap。
EnumSet :枚举类型的高性能 Set实现。它要求放入它的枚举常量必须属于同一枚举类型。
EnumMap :专门为枚举类型量身定做的 Map 实现。虽然使用其它的 Map 实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用 EnumMap 会更加高效,因为它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值。这使得 EnumMap 的效率非常高。
public class EnumTest {
public static void main(String[] args) {
//EnumSet的使用
//把Signal枚举中所有对象抽取到Set集合中
EnumSet<Signal> signalSet = EnumSet.allOf(Signal.class);
for (Enum<Signal> en : signalSet) {
System.out.println(en);
}
//EnumMap的使用
EnumMap<Signal,Object> enumMap = new EnumMap<>(Signal.class);
enumMap.put(Signal.RED, "红灯");
enumMap.put(Signal.YELLOW, "黄灯");
enumMap.put(Signal.GREEN, "绿灯");
//把所有的映射关系对象抽取到Set集合中
Set<Entry<Signal, Object>> entrySet = enumMap.entrySet();
for (Entry<Signal, Object> entry : entrySet) {
Signal key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + " -- " + value);
}
}
}
enum Signal{RED, YELLOW, GREEN}