Java1.5发行版本中增加了新的引用类型--枚举类型(enum type)。枚举类型是指由一组固定的常量组成合法值的类型在Java虚拟机中,枚举类是以用语法糖实现的,在进行编译时会转变成普通的Java类。

        创建枚举类型要使用enum关键字,隐含了所创建的类型都是java.lang.Enum类的子类(java.lang.Enum是一个抽象类)。枚举类型的通用格式为Class Enum<E extends Enum<E>>,而E表示枚举类型的名称。枚举类型的每一个值都会映射到protected Enum(String name,int ordinal)构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。

/**
 * This is the common base class of all Java language enumeration types.
*/
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
    private final String name;
    private final int ordinal;
    /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to enum type declarations.
    */
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
}

       Java的枚举类型是功能十分齐全的类,其本质上是int值。Java枚举类型背后的基本思想是:通过公有的静态final域为每个枚举常量导出实例的类。因为没有可以访问的构造器,枚举类型是真正的final,他们是单例的泛型化,本质上是单元素枚举。枚类型是类型安全的枚举模式。

        包含同名常量的多个枚举类型可以在一个系统中和平共处,因为每个类型都有自己的命名空间。你可以增加或重新排列枚举类型中的常量,而无需重新编译它的客户端代码,因为导出常量的域在枚举类型和它的客户端之间提供了一个隔离层:常量值并没有被编译到客户端代码中,而是在int枚举模式之中。

        除了完善int枚举模式的不足之外,枚举类型还允许添加任意的方法和域,并实现任意接口,他们提供了所有Object方法、Comparable接口、Serializable接口的高级实现,并针对枚举类型的可任意改变设计了序列化方式。

       接下讲解下枚举类型的用途:

用法1:常量

在JDK1.5以前,我们定义一个常量通常是 public static final xxx。在JDK1.5以后,就可以用枚举类型来定义常量。

//定义季节
public enum Season{
    SPRING,SUMMER,AUTUMN,WINTER;
}

用法2:switch

JDK1.6之前的switch语句只支持byte,short,char,int,enum类型,使用枚举,能让我们的代码可读性更强。

public class EnumTest{
    Season season = Season.SPRING;
    switch(season){
        case SPRING:
            system.out.println("SPRING");
            break;
        case SUMMER:
            system.out.println("SUMMER");
            break;
        case AUTUMN:
            system.out.println("AUTUMN");
            break;
        case WINTER:
            system.out.println("WINTER");
            break;
    }
}
enum Season{
    SPRING,SUMMER,AUTUMN,WINTER;
}

用法3:向枚举中添加新方法

如果自己定义方法,那么就必须在enum实例序列的最后添加一个分号,而且Java要求必须先定义enum实例。

public class EnumTest {
	public static void main(String[] args) {
		// 1.value()
		// 返回season所有的枚举类对象,以数组的形式返回
		Season[] s = Season.values();
		for (int i = 0; i < s.length; i++) {
			System.out.println(s[i]);
		}
	}
}

enum Season {
	// 创建枚举类的对象
	SPRING("spring", "春暖花开"), SUMMER("summer", "夏日炎炎"), AUTUMN("autumn", "秋高气爽"), WINTER("winter", "白雪皑皑"); 

	// 1.提供类的属性,声明为private final
	private final String seasonName;
	private final String seasonDesc;

	// 2.声明为final的属性,在构造器中初始化
	private Season(String seasonName, String seasonDesc) {
		this.seasonName = seasonName;
		this.seasonDesc = seasonDesc;
	}

	public String getSeasonName() {
		return seasonName;
	}

	public String getSeasonDesc() {
		return seasonDesc;
	}
}

用法4:覆盖枚举方法

enum Season {
	// 创建枚举类的对象
	SPRING("spring", "春暖花开"),SUMMER("summer", "夏日炎炎"), AUTUMN("autumn", "秋高气爽"), WINTER("winter", "白雪皑皑"); 

	// 1.提供类的属性,声明为private final
	private final String seasonName;
	private final String seasonDesc;

	// 2.声明为final的属性,在构造器中初始化
	private Season(String seasonName, String seasonDesc) {
		this.seasonName = seasonName;
		this.seasonDesc = seasonDesc;
	}

	public String getSeasonName() {
		return seasonName;
	}

	public String getSeasonDesc() {
		return seasonDesc;
	}

	@Override
	public String toString() {
		return "Season [seasonName=" + seasonName + ", seasonDesc="
				+ seasonDesc + "]";
	}
}

用法5:实现接口

所有的枚举都继承自ajva.lang.Enum类,由于Java不支持多继承,所以枚举对象不能再继承其他类。

interface Info {
	void show();
}

enum Season implements Info {
	// 创建枚举类的对象
	SPRING("spring", "春暖花开") {
		public void show() {
			System.out.println("春天");
		}
	},

	SUMMER("summer", "夏日炎炎") {
		public void show() {
			System.out.println("夏天");
		}
	},

	AUTUMN("autumn", "秋高气爽") {
		public void show() {
			System.out.println("秋天");
		}
	},

	WINTER("winter", "白雪皑皑") {
		public void show() {
			System.out.println("冬天");
		}
	};

	// 1.提供类的属性,声明为private final
	private final String seasonName;
	private final String seasonDesc;

	// 2.声明为final的属性,在构造器中初始化
	private Season(String seasonName, String seasonDesc) {
		this.seasonName = seasonName;
		this.seasonDesc = seasonDesc;
	}

	public String getSeasonName() {
		return seasonName;
	}

	public String getSeasonDesc() {
		return seasonDesc;
	}

	@Override
	public String toString() {
		return "Season [seasonName=" + seasonName + ", seasonDesc="
				+ seasonDesc + "]";
	}
}

用法6:枚举集合(EnumSet,EnumMap)的使用

java.util.EnumSet和java.util.EnumMap是两个枚举集合。

        EnumSet保证集合中的元素不重复,有效地表示从单个枚举类型中提取的多个值的多个集合,这个类实现了Set接口。在内部具体的实现上,每个EnumSet内容都表示为位矢量。如果底层的枚举类型有64个或更少的元素,整个EnumSet就是用单个long来表示,因此它的性能比得上位域(位域是让你用OR运算将几个常量合并到一个集合中,如:text.applySeason(SPRING | SUMMER))的的性能。即用EnumSet代替位域。 

public class EnumSetTest{
    private enum Season{SPRING,SUMMER,AUTUMN,WINTER};
    public void applySeason(Set<Season> seasons){
        ...
    }
    public static void main(){
        EnumSetTest enumSetTest = new EnumSetTest();
        enumSetTest.applySeason(EnumSet.of(Season.SPRING,Season.WINTER));
    }
}

        EnumSet类集位域的简洁和性能优势于一身,所以没有理由用位域来表示了。但EnumSet有个缺点,就是无法创建不可变的EnumSet。可以用Collections.unmodifiableSet将EnumSet封装起来,但简洁性和性能会受到影响。
        EnumMap中的key是enum类型,而value可以是任意类型。EnumMap在运行速度方面上能与通过序数索引的数组相媲美,是因为EnumMap在内部使用了这种数组。但是他对程序员隐藏了这种实现细节,集Map的丰富功能和类型安全与数组的快速于一身。注意EnumMap构造器采用键类型的Class对象。总之,最好不要用序数来索引数组,而要使用EnumMap。如果你所表示的这种关系是多维的,就使用EnumMap<...,EnumMap<...>>。EnumMap具体实现与用法参考源码以及JDK文档。

enum对象常用方法介绍:

int compareTo(E e):   比较此枚举与指定对象的顺序。
     Class<E> getDeclaringClass():返回与此枚举常量的枚举类型对应的Class对象。    String name():返回此枚举常量的名称
     int ordinal():返回枚举常量的序数
     String toString():返回枚举常量的名称,它包含在声明中。
     static <T extends Enum<T>> T valueOf(Class<T> enumType,String name):返回带指定名称的指定枚举类型的枚举常量。

        什么时候应该使用枚举呢?当需要一组固定常量或者包括“天然的枚举类型”,例如行星。季节等。但它也包括你在编译时就知道其所有可能值的其他集合,例如菜单的选项等。枚举类型中的常量集并不一定要始终保持不变,专门设计枚举特性是考虑到枚举类型的二进制兼容演变。

        总之,枚举天生是不可变的,所有的域都应该为final,他们可以是公有的,也可以是私有的,但最好是私有的。与int常量相比,枚举有个小小的性能缺点,即装载和初始化时会有空间和时间的成本,但枚举类易读,也更加安全,功能更加强大。枚举类型对象的值可以直接使用==进行比较,无需使用equals方法