转载请注明原文地址:http://www.javashuo.com/article/p-etnppalp-em.htmlhtml
一、定义和组织常量 java
在JDK1.5以前,咱们定义常量都是:public static fianl....。有了枚举以后,咱们能够把相关的常量定义到一个枚举类里,并且枚举类也提供了比常量更多的操做方法来操纵。安全
用法举例:ide
public enum EnumTest { MON, TUE, WED, THU, FRI, SAT, SUN; }
看不懂不要紧,上面的枚举类定义原理在下文中再解释。函数
二、用于switch测试
switch语句只支持常量值做为判断依据,枚举类型是个特例。this
用法举例:spa
//定义枚举 enum Signal { GREEN, YELLOW, RED } //测试 public class TrafficLight { Signal color = Signal.RED; public void change() { switch (color) { //使用枚举来做比较 case RED: color = Signal.GREEN; break; case YELLOW: color = Signal.RED; break; case GREEN: color = Signal.YELLOW; break; } } }
三、向枚举中自定义属性和方法线程
咱们在定义枚举时,实际上是在定义一个Enum类的子类,咱们能够往其中添加自定义的属性和方法。code
可是要注意:咱们须要在枚举类中先定义enum实例,用分号 ; 隔开。而后才是成员变量和方法的定义。若是顺序错了会致使编译错误。【实际开发时有可能反过来,先定义成员变量和方法,再添加枚举实例】
用法举例:
public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); //定义枚举实例 // 自定义成员变量 private String name; private int index; // 定义构造方法 private Color(String name, int index) { this.name = name; this.index = index; } // 添加方法 public static String getName(int index) { for (Color c : Color.values()) { if (c.getIndex() == index) { return c.name; } } return null; } // get set 方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } }
四、重写Enum类中的方法
定义枚举类的过程实际上是定义Enum类的子类的过程,Enum类定义了一些方法,这些方法咱们能够在本身定义枚举类时重写,最多见的是:重写toString()函数,返回自定义成员变量的拼接结果。
用法举例:
public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 自定义成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } //重写父类方法 @Override public String toString() { return this.index+"_"+this.name; } }
五、实现接口
前面提到,用enum关键字定义枚举类时,实际上是定义Enum类的子类。也就是说,咱们定义的枚举类是继承自 Enum类的。
而Java中是不支持多继承的,那若是多个枚举类型都有通用的行为时,如何进行抽象组织呢?——答案是:实现接口。
咱们能够在定义枚举类时,实现接口,重写接口中的方法来达到增长行为的目的。
//定义接口 public interface Behaviour { void print(); String getInfo(); } //定义枚举类,实现接口 public enum Color implements Behaviour{ RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } //接口方法 @Override public String getInfo() { return this.name; } //接口方法 @Override public void print() { System.out.println(this.index+":"+this.name); } }
六、EnumSet的使用
java.util.EnumSet是枚举类型的集合类,能够保证集合中的枚举元素不重复。
// EnumSet的使用 EnumSet<EnumTest> weekSet = EnumSet.allOf(aEnum.class); //将枚举类.class文件做为参数。至于为什么使用.class文件,在下文中揭晓
七、EnumMap的使用
java.util.EnumMap中的 key是enum类型,而value则能够是任意类型。
// EnumMap的使用 EnumMap<自定义Enum类型, String> weekMap = new EnumMap(aEnum.class); weekMap.put(aEnum.MON, "星期一"); weekMap.put(aEnumt.TUE, "星期二");
enum这个关键字,包含了:继承Enum类,定义当前类 的意思,所建立的类型都是 java.lang.Enum 类的子类。
虽然都是定义类,可是enum关键字和class关键字的约束行为不一样,class定义的类,经过new操做建立对象,想new几个就几个,而enum关键字定义的类,其实例对象,只能在这个enum类中定义好,它的实例是有限的,限制了某些东西的范围。
若是咱们不自定义枚举类的成员变量和构造方法,只定义枚举实例,则枚举实例内容都将以字符串的形式存在,在类加载的时候会经过 protected Enum(String name, int ordinal)
构造函数被建立为基本的Enum实例。
回到第一点中的第一个用法:
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); ... ...
也就是说,这段代码实际上调用了7次 Enum(String name, int ordinal)。
枚举类通过编译器编译以后产生的是一个class文件,该class文件通过反编译能够看到其实是生成了一个类,该类继承了java.lang.Enum<E>。
也就是说,enum 实际上就是一个 class,只不过 java 编译器帮咱们作了语法的解析和编译而已,咱们的枚举值也被解释成了static final修饰的常量。
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类中封装了一些方法,最经常使用的是 compareTo 和 toString。
int compareTo(E o) 比较此枚举与指定对象的顺序。 Class<E> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象。 String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明。 int ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 String toString() 返回枚举常量的名称,它包含在声明中。 static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。
参考个人另外一片博文;http://www.javashuo.com/article/p-xfppzqug-dv.html
枚举实例可使用 == 做比较,也能够 使用 equals() 做比较,两者结果同样,由于Enum类重写了 equals()方法,其实质仍是使用 == 做比较的。
public final boolean equals(Object other) { return this==other; }
来看一则 switch中用枚举做case的代码反编译结果:
package com.example.demo; import java.io.PrintStream; ab public class EnumTest{ public EnumTest(){} public static transient void main(string args[]) {ab a = ab.aaa; class _anm1 {} switch(_cls1..SwitchMap.com.example.demo.ab[a.ordinal()]) //取的是枚举的ordinal()方法的返回值 { case 1: // '\001'system.out.println("aaa"); // fall through case 2: // '\002'system.out.println("bbb"); // fall through default:return; } }}
结合上文中第四点Enum类的解读,ordinal()方法返回的是枚举类中的ordinal成员变量值(一个int值),所以switch中用枚举类型做比较时其实是用枚举值的ordinal值做比较的。
枚举类型在序列化时仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是经过java.lang.Enum的valueOf方法来根据名字查找枚举对象。
同时,编译器禁止重写枚举类型的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
从第二点,枚举类反编译获得的代码咱们能够看到,枚举类编译出来的属性都是static类型的,而static类型的属性会在类被加载以后初始化,而Java类的加载和初始化过程都是线程安全的,因此建立一个enum类型是线程安全的。