枚举定义
枚举(enum)是一种特殊的类,用于定义一组命名的常量值。每个常量值都有一个相关联的名称。在程序中使用枚举时,可以使用这些名称来表示特定的枚举值,而不必记住它们的具体值,每个枚举值都是作为Enum
类的实例在内部实现的,且所有的枚举值都是 public static final 的。在java中枚举的定义格式如下,enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口。
在Java引入枚举类型之前使用的是int 或string 枚举模式。这类模式存在比较大的弊端,这类模式具有类型不安全性,即使传入了不期望的枚举值,编译器也不会报错。而枚举类型(Enum)则不会有这类问题。
//在java中枚举的定义格式
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
//可以直接打印枚举名称
System.out.println(Color.GREEN);
关于枚举的使用
-
在java中枚举是一种特殊的引用类型,你可以像使用其他Java对象一样使用枚举成员。你可以将它们赋值给变量,或者作为方法的参数或返回值。在枚举中也可以定义构造器和方法,构造器会在枚举成员被创建时调用,可以用来对枚举对象进行初始化等操作。在枚举类中定义方法时需要注意,如果定义的是具体方法则每个枚举成员都可以调用该方法,如果是抽象方法则每个枚举成员都必须实现该方法。
-
枚举继承了Enum类,其中有values(), valueOf(),name()和ordinal()等方法可以直接使用。
- values() 返回枚举类中所有的值
- valueOf() 返回指定字符串值的枚举常量
- name() 返回枚举常量的名称,它与使用
toString()
方法得到的结果相同 - ordinal() 可以找到每个枚举常量的索引,类似数组索引
-
枚举类型的比较通常使用
==
操作符,因为枚举常量在 JVM 中是唯一的。使用equals()
也可以,不过==
性能更快,如果需要比较的是枚举类型的对象而不是常量,那么可以使用equals()
方法,但这种情况并不常见。
枚举应用示例
用两个简单的示例来展示一下枚举强大的功能
- 这个示例是为了打印出一个物体在不同天体上的重量,只需在枚举中给定每个行星的质量和半径,在构造器中便会计算行星表面的重力。在以后如果需要知道其他天体的情况也只需在枚举里加上该天体即可,后续的维护得到了很大的简化。
//Effective java 实例
//行星枚举
public enum Planet {
MERCURY(3.302e+23,2.439e6),
VENUS(4.869e+24,6.052e6),
EARTH(5.975e+24,6.378e6),
MARS(6.419e+23,3.393e6),
JUPITER(1.899e+27,7.149e7),
SATURN(5.685e+26,6.027e7),
URANUS(8.683e+25,2.556e7),
NEPTUNE(1.024e+26,2.477e7);
//质量
private final double mass;
//半径
private final double radius;
//重力
private final double surfaceGravity;
private static final double G = 6.67300E-11;
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass/(radius*radius);
}
public double getMass() {
return mass;
}
public double getRadius() {
return radius;
}
public double getSurfaceGravity() {
return surfaceGravity;
}
public double surfaceWeight(double mass){
return mass * surfaceGravity;
}
}
//快速打印出一个物体在不同天体上的重量
public class PlanetTest {
public static void main(String[] args) {
double earthWeight = Double.parseDouble("100.00");
double mass = earthWeight / Planet.EARTH.getSurfaceGravity();
for(Planet p : Planet.values()){
System.out.println("在"+p+"的重量是"+p.surfaceWeight(mass));
}
}
}
- 下面是一个策略枚举的示例。这里面的策略指的是策略模式,在策略枚举中各种策略被包装成枚举,每个枚举都会有同个动作,但每个枚举所做的事均由每个枚举自身定义。下面示例中需要实现在一周内都需要吃什么,在不使用策略枚举时我们一般用if-else或switch来实现(若有大量if-else将会使代码变的不易读,后续的维护也会非常痛苦)。用了策略枚举后便可消除大量的if-else,提升代码的可读性和可扩展性。
public enum DayEnum {
Monday {
@Override
public String toEat() {
return "今天吃芒果";
}
},
Tuesday {
@Override
public String toEat() {
return "今天吃葡萄";
}
},
Wednesday {
@Override
public String toEat() {
return "今天吃草莓";
}
},
Thursday {
@Override
public String toEat() {
return "今天吃菠萝";
}
};
public abstract String toEat();
}
//使用时只需调用toEat()方法便可
DayEnum.Monday.toEat()
枚举的特点和注意事项
- Java枚举类型通过公有的静态final域为每个枚举常量导出一个实例。枚举类型没有可以进行访问的构造器,是一个final 类。枚举类型不能创建枚举类型的实例,也不能对其进行扩展,只存在声明过的枚举变量。也就是说枚举类型是实例受控的,是单例的泛型化。
- 每当需要一组固定常量,并且在编译时就知道其成员的时候,就应该使用枚举。但枚举类型中的常量集并不一定要始终保持不变。
- 如果多个枚举常量同时共享相同的行为,则要考虑策略枚举。
- 枚举类型都有一个ordinal()方法,这个方法能返回每个枚举在类型中的数字位置。Enum规范在谈及ordinal方法中说到,大多数程序员都不需要这个方法,它是设计用于像EnumSet 和 EnumMap这种基于枚举的通用数据结构的。因为如果对常量重新排序,枚举的顺序可能会改变,所以在平常的使用中不要根据枚举的序数导出与其关联的值,而是要将它保存在一个实例域中。
- 在数据库中存储枚举值时,通常有两种方法:一是存储枚举的name()字符串表示,二是存储一个整数(通常是枚举声明中的位置索引)。存储枚举名比较直观以及不会受枚举的顺序改变的影响,但是字符串占用空间比整数大,并且字符串比较通常比整数比较要慢,尤其是在数据库查询时。存储整数虽有性能和空间方面的优势,但也有一个很大的劣势,当枚举的定义顺序发生改变时,数据库存储的整数将会和枚举现有的值无法正确对应,导致数据不一致。并且存储整数的直观性不如使用枚举名。因此,在选择存储方式时,需要根据具体情况权衡两种方法的优缺点。
- 使用Spring,Hibernate框架时可以使用
@Enumerated
注解,可以选择以枚举名(EnumType.STRING
)或序数(EnumType.ORDINAL
)的形式来存储枚举值。