在学习枚举和注解章节之前,我们先来看一下枚举和注解的基础知识。
1.枚举类的方法。
2.枚举的简单使用。
1.枚举类的方法。
我们所有的枚举实际上是继承一个枚举类(java.lang.Enum),这个类本身提供了很多方法供继承他的枚举使用,我们来看一下这些方法的作用。
a. 我们先看下面代码,首先我们可以知道枚举类本身是一个抽象的类,他无法直接创建对象,所有枚举必须继承这个抽象类。他还实现了Comparable和序列化的接口,是支持比较和序列化的。
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable
b.ordinal()
定义了枚举后会有一定的顺序,这个方法返回的是实例在声明时的次序。
c.valueOf()
看下面源码,他是一个静态的方法,通过它可以通过传入枚举的name得到枚举对象。
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);
}
看下面源码,枚举为了保证枚举的唯一性,重写了readObject,阻止枚举实例在序列化的时候生成另外的对象。枚举的序列化和反序列化是直接通过name来进行的。序列化时通过传入name和反序列化是把name传入到valueOf()方法中来得到实例对象。
/**
* prevent default deserialization
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
这个方法可以得到所有的枚举实例,但是我在枚举的抽象类中没有发现这个方法。是编译器在编译枚举类的时候添加上去的。
2.枚举的简单使用。
a. 看下面代码,定义了一个颜色的枚举。首先Color是继承上面的抽象枚举类,他自己加了两个成员变量。四个颜色对象分别调用自定义的构造方法生成四个枚举对象。
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 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;
}
}
b. switch语句中可以使用枚举,例子就不写了,能使用的原因是switch只能使用整数值,枚举本身可以通过ordinal来得到整数次序。
c. EnumMap和EnumSet
我们先看一下EnumMap的用法,看下面代码,Size枚举类型,EnumMap的key是枚举类型,初始化的时候需要传递类型信息,如下面例子,传递Size.class。
Map<Size, Integer> map = new EnumMap<>(Size.class);
为什么我们不直接使用HashMap了,枚举完全可以作为HashMap的Key。EnumMap是专门为枚举为Key的Map专门设计的,他有两个优点,一个是内部按顺序保存,输出的时候同样有顺序,另一个是比HashMap更加高效。高效的原因是EnumMap用两个数组来保存,一个保存枚举Key,另一个保存value,根据枚举很容易得到序列(枚举是有序列的),再根据序列很快可以找到Value。
总的来说,EnumMap根据枚举的特性来实现的,他可以高效的存取,如果你需要用到Map,枚举是Key,这个时候你需要考虑EnumMap。
我们再来看一下EnumSet的用法,看下面代码,EnumSet是一个抽象的类不能直接创建,只能利用工厂方法它提供的工厂方法来创建。
EnumSet<Size> set = new EnumSet<Size>(); 错误的
Set<Day> weekend = EnumSet.noneOf(Day.class); 正确的,创建一个空的Day类型的枚举集合
看一下noneOf的源码,RegularEnumSet和JumboEnumSet实现了EnumSet这个抽象类。枚举数量大于64用JumboEnumSet。这是内部自动控制的。
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
我们简单说一下EnumSet的实现原理,有兴趣的可以自己看下源码。EnumSet和HashSet不一样,HashSet是基于HashMap来实现的。EnumSet对于EnumMap完全是另一种实现。它采用的是位向量实现的。位向量定义:一个位表示一个元素的状态,一组位表示一个集合的状态,每个位对应一个元素,而状态只可能有两种。我们简单的看一例子,比如我们有一个星期的集合,集合中包括周一到周日,下面一组位向量就可以表示一个星期集合的状态,这个集合包含哪些周一,周三,周五,周日。并且EnumSet的所有的计算都是通过位向量的计算来完成的,非常的高效。
1 | 0 | 1 | 0 | 1 | 0 | 1 |