枚举在很多编程语言中都有,例如 C /C++,但 Java 直到 JDK1.5 才减少这个个性,至于为什么那么晚,我就不得而知了。那什么是枚举呢?在维基百科上有如下定义:在数学和计算机科学实践中,一个集的 枚举 是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。例如示意星期的 SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY 等。
1 Java 中的枚举
1.1 简略应用
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)的写法就是在调用结构器创立枚举实例,而且这个实例是单例的,这也是为什么有的文章说应用枚举来实现单例模式是最简略的(尽管是最简略,但并不是最实用的,因为可读性较差)。写一个类,必定是要应用的,上面代码展现了枚举类的应用:
《2020 最新 Java 根底精讲视频教程和学习路线!》
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” 只是该枚举类的一个成员变量,这里不了解没关系,等会看源码的时候就明确了。
1.2 枚举中的形象办法
除了简略应用之外,咱们还能够在枚举类中退出形象办法,每个实例都必须实现这个形象办法,否则会编译失败,如下所示:
// 其余代码和原先齐全一样
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()形象办法,每个枚举实例都必须实现这个办法且每个枚举实例都能够有本人的实现,这是枚举类灵活性的体现。这个个性在有些场景下十分有用,例如如果有一个枚举类用来示意四则运算的操作,应用形象办法,该办法有两个参数,而后不同的运算操作就能够依据本人的个性实现不同的运算。
枚举其实还是很多灵便的用法,在此不再多说了。
2 Enum 类解析
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 办法,默认状况下,该办法会间接抛出异样,阻止反序列化,咱们能够通过重写该办法来关上反序列化的开关,但要小心一些,因为反序列化操作会毁坏枚举实例的单例个性,可能会导致虚拟机中的枚举实例不惟一。
其余办法例如 equal,compareTo 什么的就不多说了,都是套路。
3 小结
枚举也是一种很重要的组件,性能很弱小,灵便,但很多开发者可能会小看他,认为其不过就是申明了一些枚举常量而已。这的确是枚举最基本的作用,但实际上,Java 枚举还有很多其余弱小的性能,例如能够申明形象办法,能够轻而易举的保障线程平安,保障单例等等。