申明:本帖为对网上资源进行整理总结并结合自己的理解后重写的帖子,部分引用的源资源链接如下:
- https://www.cnblogs.com/jingmoxukong/p/6098351.html (该贴写的很好,借鉴较多)
- https://www.jianshu.com/p/7d3e3f6695a5
- https://blog.csdn.net/congqingbin/article/details/7520137
一、Enum (enumeration)
枚举是在JDK1.5引入了新特性,在项目中经常会通过:Public static final ....来定义常量,有了枚举类之后,我们可以将同类型常量分组到一个枚举类,方便统一管理和使用。
枚举看起来像是一种新的数据类型,实际上和正常的类相似,是一种受限的类。可以有实例变量,实例方法,静态方法,构造函数,抽象方法等等,需要注意的是:
- Enum的构造方法为私有的,不能通过new的方法来创建,它的实例个数是有限的,不允许使用赋值符号 = 来为枚举常量赋值。枚举类不能继承,因为Enum类本身继承于 Java.lang.Enum , java语言本身不支持多重继承,所以 enum 类不能再继承其他类,当然也不能继承另一个枚举类。
- 所有的枚举值都是 public ,static ,final的。
- Enum 默认实现了Java.lang.Comparable 和 Serializable 接口,所以也提供 compareTo( )方法。
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable { ... }
PS:如果要为enum定义方法,那么必须在enum的最后一个实例尾部添加一个分号。此外,在enum中,必须先定义实例,不能将字段或方法定义在实例前面。否则,编译器会报错。
public enum CarType {
//自定义枚举元素
BMW(1,"宝马"),
BENZ(2,"奔驰"),
LINCOLN(3,"林肯"),
ROLLS_ROYCE(4,"劳斯莱斯"),
AUDI(5,"奥迪"),
FERRARI(6,"法拉利");
private int value;
private String name;
//枚举类的构造方法,注意:枚举类的构造方法只能用private来修饰
private CarType(int value, String name)
{
this.value = value;
this.name = name;
}
//枚举类的普通方法
public int getValue() {
return value;
}
public String getName(){
return name;
}
//抽象方法
public abstract String getDescription();
//静态方法
public static void main(String[] args) {
for (CarType car :CarType.values() ) {
System.out.println(car.name());
}
}
二、枚举类的常用方法
- values( ) 以数组的形式返回枚举类型的所有成员。 理论上此方法可以将枚举类转变为一个枚举类型的数组,因为枚举中没有下标,我们没有办法通过下标来快速找到需要的枚举类,这时候,转变为数组之后,我们就可以通过数组的下标,来找到我们需要的枚举类。
使用实例:
2.valueOf( ) 将指定的普通字符串转化为枚举类型的实例
3.name( ) 返回枚举类型实例名
ordinal( ) 返回实例申明时的次序,从0开始
三、枚举类使用场景
1. 常量
public enum Signal{ RED, YELLOW, GREEN}
2. Switch 状态机
因为Switch支持 int , char , String , enum类型参数,所以我们可以用 enum 来做分支判断:
enum Signal {RED, YELLOW, GREEN}
public static String getTrafficInstruct(Signal signal) {
String instruct = "信号灯故障";
switch (signal) {
case RED:
instruct = "红灯停";
break;
case YELLOW:
instruct = "黄灯请注意";
break;
case GREEN:
instruct = "绿灯行";
break;
default:
break;
}
return instruct;
}
3. 枚举类中添加新的方法,以及EnumSet、EnumMap (常用方法)
Java 中提供了两个方便操作enum的工具类——EnumSet 和 EnumMap。
EnumSet
是枚举类型的高性能 Set
实现。它要求放入它的枚举常量必须属于同一枚举类型。EnumMap
是专门为枚举类型量身定做的 Map
实现。虽然使用其它的 Map 实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用 EnumMap 会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值。这使得 EnumMap 的效率非常高。
public enum CarType {
//枚举元素实例
BMW(1,"宝马"),
BENZ(2,"奔驰"),
LINCOLN(3,"林肯"),
ROLLS_ROYCE(4,"劳斯莱斯"),
AUDI(5,"奥迪"),
FERRARI(6,"法拉利");
private int value;
private String name;
CarType(int value, String name)
{
this.value = value;
this.name = name;
}
public int getValue() {
return value;
}
public String getName(){
return name;
}
//将枚举值存入Map中,然后通过Map的 Key-value 键值对来获取想要的值
// 方法一:
private static Map<Integer, CarType> valueMap = Stream.of(CarType.values())
.collect(Collectors.toMap(CarType::getValue, Function.identity()));
public static String getCarTypeById(int id){
if(id == 0){
return null;
}
return valueMap.get(id).toString();
}
// 方法二:
private static Map<Integer, CarType> idToCarTypeMap = new HashMap<>();
static {
Arrays.stream(values()).forEach(carType -> {
idToCarTypeMap.put(carType.value,carType);
});
}
public static CarType getCarType(int id){
return idToCarTypeMap.get(id);
}
public static String getNameById(int id){
if(id == 0){
return null;
}else{
CarType carType = idToCarTypeMap.get(id);
String carTypeName = Optional.ofNullable(carType).map(CarType::getName).orElse("入参ID无效,无法获取指定汽车类型" );
return carTypeName;
}
}
// EnumSet的用法:
System.out.println("EnumSet展示");
EnumSet<ErrorCodeEn> errSet = EnumSet.allOf(ErrorCodeEn.class);
for (ErrorCodeEn e : errSet) {
System.out.println(e.name() + " : " + e.ordinal());
}
// EnumMap的使用
System.out.println("EnumMap展示");
EnumMap<StateMachine.Signal, String> errMap = new EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED, "红灯");
errMap.put(StateMachine.Signal.YELLOW, "黄灯");
errMap.put(StateMachine.Signal.GREEN, "绿灯");
for (Iterator<Map.Entry<StateMachine.Signal, String>> iter = errMap.entrySet().iterator(); iter.hasNext();) {
Map.Entry<StateMachine.Signal, String> entry = iter.next();
System.out.println(entry.getKey().name() + " : " + entry.getValue());
}
}
4. 枚举类实现接口及组织枚举
可以将类型相近的枚举通过接口或者类组织起来。
但是一般用接口方式进行组织。
原因是:Java接口在编译时会自动为enum类型加上public static
修饰符;Java类在编译时会自动为 enum
类型加上static修饰符。看出差异了吗?没错,就是说,在类中组织 enum
,如果你不给它修饰为 public
,那么只能在本包中进行访问。
// 在接口中组织 enum
public interface INumberEnum {
int getCode();
String getDescription();
}
public enum ErrorCodeEn2 implements INumberEnum {
OK(0, "成功"),
ERROR_A(100, "错误A"),
ERROR_B(200, "错误B");
ErrorCodeEn2(int number, String description) {
this.code = number;
this.description = description;
}
private int code;
private String description;
@Override
public int getCode() {
return code;
}
@Override
public String getDescription() {
return description;
}
}
// 在类中组织枚举
public interface Plant {
enum Vegetable implements INumberEnum {
POTATO(0, "土豆"),
TOMATO(0, "西红柿");
Vegetable(int number, String description) {
this.code = number;
this.description = description;
}
private int code;
private String description;
@Override
public int getCode() {
return 0;
}
@Override
public String getDescription() {
return null;
}
}
enum Fruit implements INumberEnum {
APPLE(0, "苹果"),
ORANGE(0, "桔子"),
BANANA(0, "香蕉");
Fruit(int number, String description) {
this.code = number;
this.description = description;
}
private int code;
private String description;
@Override
public int getCode() {
return 0;
}
@Override
public String getDescription() {
return null;
}
}
}
5. 策略枚举(很经典的用法)
EffectiveJava中展示了一种策略枚举。这种枚举通过枚举嵌套枚举的方式,将枚举常量分类处理。
这种做法虽然没有switch语句简洁,但是更加安全、灵活。
enum PayrollDay {
MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(
PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(
PayType.WEEKEND), SUNDAY(PayType.WEEKEND);
private final PayType payType;
PayrollDay(PayType payType) {
this.payType = payType;
}
double pay(double hoursWorked, double payRate) {
return payType.pay(hoursWorked, payRate);
}
// 策略枚举
private enum PayType {
WEEKDAY {
double overtimePay(double hours, double payRate) {
return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)
* payRate / 2;
}
},
WEEKEND {
double overtimePay(double hours, double payRate) {
return hours * payRate / 2;
}
};
private static final int HOURS_PER_SHIFT = 8;
abstract double overtimePay(double hrs, double payRate);
double pay(double hoursWorked, double payRate) {
double basePay = hoursWorked * payRate;
return basePay + overtimePay(hoursWorked, payRate);
}
}
}
//测试:
System.out.println("时薪100的人在周五工作8小时的收入:" + PayrollDay.FRIDAY.pay(8.0, 100));
System.out.println("时薪100的人在周六工作8小时的收入:" + PayrollDay.SATURDAY.pay(8.0, 100));