枚举在不少编程语言中都有,例如C/C++,但Java直到JDK1.5才增长这个特性,至于为何那么晚,我就不得而知了。那什么是枚举呢?在维基百科上有以下定义:在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的全部成员的程序,或者是一种特定类型对象的计数。例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY等。java
Java中各类组件都是类,枚举也不例外。要建立枚举类,只须要将普通类的class关键字替换成enum关键字便可,以下所示:编程
public enum ColorEnum {
RED("RED", 0),
BLUE("BLUE", 1),
BLACK("BLACK",2),
WHITE("WHITE", 3);
ColorEnum(String name, int code) {
this.name = name;
this.code = code;
}
private String name;
private int code;
//getter and setter
}
复制代码
枚举类也能够有成员变量、构造器、实例方法、静态方法,但构造器只能是私有的,即外部程序没法使用构造器来构造枚举实例,只能在实例类内部建立,例如RED("RED",0)的写法就是在调用构造器建立枚举实例,并且这个实例是单例的,这也是为何有的文章说使用枚举来实现单例模式是最简单的(虽然是最简单,但并非最实用的,由于可读性较差)。写一个类,确定是要使用的,下面代码展现了枚举类的使用:安全
public class Test {
public static void main(String[] args) {
System.out.println(ColorEnum.BLACK); //直接调用实例
System.out.println(ColorEnum.BLACK.getName()); //调用实例的getName()方法
System.out.println(ColorEnum.BLACK.getCode()); //调用实例的getCode()方法
System.out.println(ColorEnum.valueOf("BLACK")); //valueOf()方法根据枚举的名字获取枚举实例
System.out.println("----------------------------");
for (ColorEnum colorEnum : ColorEnum.values()) { //values()方法返回该枚举类的全部枚举实例
System.out.println(colorEnum.getName());
System.out.println(colorEnum.ordinal()); //ordinal返回该枚举实例在枚举类中声明的顺序,从0开始
}
}
}
复制代码
注释写的比较清楚了,惟一让人迷惑的就是valueOf(String)方法,该方法接受的参数是枚举实例的名字,注意这里的名字不是指的name成员变量,而是该实例自己的名字,例如在ColorEnum中的RED("RED",0)声明,最外面的RED就是该实例自己的名字,字符串"RED"只是该枚举类的一个成员变量,这里不理解不要紧,等会看源码的时候就明白了。编程语言
除了简单使用以外,咱们还能够在枚举类中加入抽象方法,每一个实例都必须实现这个抽象方法,不然会编译失败,以下所示:ide
//其余代码和原先彻底同样
RED("RED", 0) {
@Override
public void abstractMethod() {
System.out.println("RED's method");
}
},
BLUE("BLUE", 1) {
@Override
public void abstractMethod() {
System.out.println("BLUE's method");
}
},
BLACK("BLACK",2) {
@Override
public void abstractMethod() {
System.out.println("BLACK's method");
}
},
WHITE("WHITE", 3) {
@Override
public void abstractMethod() {
System.out.println("WHITE's method");
}
};
public abstract void abstractMethod();
复制代码
代码中声明了abstractMethod()抽象方法,每一个枚举实例都必须实现这个方法且每一个枚举实例均可以有本身的实现,这是枚举类灵活性的体现。这个特性在有些场景下很是有用,例如若是有一个枚举类用来表示四则运算的操做,使用抽象方法,该方法有两个参数,而后不一样的运算操做就能够根据本身的特性实现不一样的运算。this
枚举其实仍是不少灵活的用法,在此再也不多说了。spa
Enum类是全部枚举类的父类,实现了Comparable和Serializable接口,具备可比较和可序列化的能力。其源码以下所示:线程
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
//name字段表示枚举实例的名字
private final String name;
public final String name() {
return name;
}
//ordinal字段表示枚举实例在枚举类中声明的顺序,从0开始
private final int ordinal;
public final int ordinal() {
return ordinal;
}
//构造器
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
return super.hashCode();
}
//默认不支持clone
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
protected final void finalize() { }
//默认不支持反序列化,反序列化会破坏单例模式
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}
复制代码
先来看看两个成员变量name和ordinal,name就是以前提到的枚举实例的名字,ordinal就是枚举实例在枚举类中声明的顺序,从0开始。而后就是valueOf()方法,该方法根据枚举实例的名字和枚举实例的类对象来获取对应的枚举实例,在咱们使用的时候没有传入enumType,是由于在建立枚举类的时候,Java帮咱们加入了一个重载版本,咱们通常使用该重载版本便可。最后再看看readObject()方法,默认状况下,枚举实例是能够序列化的,可是不能反序列化,由于反序列化的时候会调用readObject方法,默认状况下,该方法会直接抛出异常,阻止反序列化,咱们能够经过重写该方法来打开反序列化的开关,但要当心一些,由于反序列化操做会破坏枚举实例的单例特性,可能会致使虚拟机中的枚举实例不惟一。code
其余方法例如equal,compareTo什么的就很少说了,都是套路。对象
枚举也是一种很重要的组件,功能很强大,灵活,但不少开发者可能会小看他,认为其不过就是声明了一些枚举常量而已。这确实是枚举最根本的做用,但实际上,Java枚举还有不少其余强大的功能,例如能够声明抽象方法,能够垂手可得的保证线程安全,保证单例等等。