Enum是在java 1.5这个版本引入的, 存放于java.lang中,它是所有java枚举类型的基本类。
它的最大的一个作用就是,定义表示一组相同类型常量。
与传统的常量定义的区别
Public static class RainbowColor {
// 红橙黄绿青蓝紫七种颜色的常量定义
public static final int RED = 0;
public static final int ORANGE = 1;
public static final int YELLOW = 2;
public static final int GREEN = 3;
public static final int CYAN = 4;
public static final int BLUE = 5;
public static final int PURPLE = 6;
}
public class State {
public static final int ON = 1;
public static final int OFF= 0;
}
如上图所示,我们习惯了在接口或者是在类里面对常量进行定义,使用的时候,你可以在程序中直接引用这些常量。但是,这种方式还是存在着一些问题。
类型不安全
由于颜色常量的对应值是整数形,所以程序执行过程中很有可能给颜色变量传入一个任意的整数值,导致出现错误。
没有命名空间
由于颜色常量只是类的属性,当你使用的时候不得不通过类来访问。
一致性差
因为整形枚举属于编译期常量,所以编译过程完成后,所有客户端和服务器端引用的地方,会直接将整数值写入。这样,当你修改旧的枚举整数值后或者增加新的枚举值后,所有引用地方代码都需要重新编译,否则运行时刻就会出现错误。
类型无指意性
由于颜色枚举值仅仅是一些无任何含义的整数值,如果在运行期调试时候,你就会发现日志中有很多魔术数字,但除了程序员本身,其他人很难明白其奥秘。
而enum类型的定义方式可能让有些朋友颇为不习惯,有时候让人觉得它就像是关键字class,又让人觉得它是一种嵌套在类里面的符合方法,我们先来看一看enum的语法是怎么定义的:
创建枚举类型要使用 enum 关键字,隐含了所创建的类型都是 java.lang.Enum 类的子类
(java.lang.Enum 是一个抽象类)。枚举类型符合通用模式 Class Enum<E extends Enum<E>>,
而 E 表示枚举类型的名称。枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal)
构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。
因此我们写一段代码来看看这个语法是什么意思:
public enum EnumTest {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
这段代码实际上是实现了以下过程:
new Enum<EnumTest>("MON",0);
new Enum<EnumTest>("TUE",1);
new Enum<EnumTest>("WED",2);
enum常量类型常量的使用有以下好处:
- 用enum常量说明的常量由编译程序自动生成,程序员不需要对常量进行手工赋值。
- 可以保证单例模式,且比较时候可以用 ”==” 来替换 equals
enum类型的使用例子
遍历,switch
public class Test {
public static void main(String[] args) {
for (EnumTest e : EnumTest.values()) {
System.out.println(e.toString());
}
EnumTest test = EnumTest.TUE;
switch (test) {
case MON:
System.out.println("今天是星期一");
break;
case TUE:
System.out.println("今天是星期二");
break;
// ... ...
default:
System.out.println(test);
break;
}
}
}
EnumSet,EnumMap 的应用
public class Test {
public static void main(String[] args) {
// EnumSet的使用
EnumSet<EnumTest> weekSet = EnumSet.allOf(EnumTest.class);
for (EnumTest day : weekSet) {
System.out.println(day);
}
// EnumMap的使用
EnumMap<EnumTest, String> weekMap = new EnumMap(EnumTest.class);
weekMap.put(EnumTest.MON, "星期一");
weekMap.put(EnumTest.TUE, "星期二");
// ... ...
for (Iterator<Entry<EnumTest, String>> iter =
weekMap.entrySet().iterator(); iter.hasNext();) {
Entry<EnumTest, String> entry = iter.next();
System.out.println(entry.getKey().name() + ":" + entry.getValue());
}
}
}
enum类型注意事项
- enum 类型不支持 public 和 protected 修饰符的构造方法,因此构造函数一定要是 private 或 friendly 的。也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。这一特点保证了其单例模式和类型安全。
- 定义 enum 类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号';'隔开,不能用逗号或空格。
- 由于 enum 类型的值实际上是通过运行期构造出对象来表示的,所以在 cluster 环境下,每个虚拟机都会构造出一个同义的枚举对象。因而在做比较操作时候就需要注意,如果直接通过使用等号 ( ‘ == ’ ) 操作符,这些看似一样的枚举值一定不相等,因为这不是同一个对象实例。
- 它不能有public的构造函数,这样做可以保证客户代码没有办法新建一个enum的实例。
- 所有枚举值都是public , static , final的。注意这一点只是针对于枚举值,我们可以和在普通类里面定义变量一样定义其它任何类型的非枚举变量,这些变量可以用任何你想用的修饰符。
- Enum默认实现了java.lang.Comparable接口。
- Enum覆载了了toString方法,因此我们如果调用Color.Blue.toString()默认返回字符串”Blue”.
- Enum提供了一个valueOf方法,这个方法和toString方法是相对应的。调用valueOf(“Blue”)将返回Color.Blue.因此我们在自己重写toString方法的时候就要注意到这一点,一把来说应该相对应地重写valueOf方法。
- Enum还提供了values方法,这个方法使你能够方便的遍历所有的枚举值。
- Enum还有一个oridinal的方法,这个方法返回枚举值在枚举类种的顺序,这个顺序根据枚举值声明的顺序而定,这里Color.Red.ordinal()返回0。
enum类型的原理
上文中已经说了,enum类型跟我们平时用的关键字class有点儿类似,你可以把它理解成另类的class,我们通过反编译代码可以看到如下的结果:
public class com.hmw.test.EnumTest extends java.lang.Enum{
public static final com.hmw.test.EnumTest MON;
public static final com.hmw.test.EnumTest TUE;
public static final com.hmw.test.EnumTest WED;
public static final com.hmw.test.EnumTest THU;
public static final com.hmw.test.EnumTest FRI;
public static final com.hmw.test.EnumTest SAT;
public static final com.hmw.test.EnumTest SUN;
static {};
public int getValue();
public boolean isRest();
public static com.hmw.test.EnumTest[] values();
public static com.hmw.test.EnumTest valueOf(java.lang.String);
com.hmw.test.EnumTest(java.lang.String, int, int, com.hmw.test.EnumTest);
}
所以事实证明,enum确实是一个类。
最后,这篇博客讲的也很好,我从中收益良多,推荐给大家。