一:枚举类
枚举是JDK1.5添加的,在枚举类型出来之前,我们都是以定义常量来代替,比如:
public class Date {
public static final int ONE = 0;
public static final int TWO = 1;
public static final int THREE = 2;
public static final int FOUR = 3;
public static final int FIVE = 4;
}
这种定义常量的方式也是有一些不足的,如int值相同的情况下,编译期并不会提示我们的,所以很容易造成混淆。
定义枚举类的一般语法:任意两个枚举成员不能具有相同的名称,多个枚举成员之间使用逗号分隔,枚举表示的类型其取值是必须有限的
public enum Date {
ONE,TWO,THREE,FOUR,FIVE
}
二:枚举类原理
把刚才定义的Date枚举类编译后再反编译class文件来看一下:
可以看到,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的ONE枚举类型对应public static final Date ONE;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。
- 枚举类实际上也是一个类,默认继承于java.lang.Enum类,并且此类是final的,不可被继承,又由于java是单继承已经默认继承Enum所以我们定义的Date枚举类是不允许继承其他类的。
- 默认生成私有构造函数,只能由虚拟机去创建,不允许外部直接创建枚举类对象的。
- 定义的枚举实际上是枚举类型的常量。
java.lang.Enum源码
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
//返回此枚举常量的名称,在其枚举声明中对其进行声明
private final String name;
public final String name() {
return name;
}
//返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
private final int ordinal;
public final int ordinal() {
return ordinal;
}
//枚举的构造方法,只能由编译器调用
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
//比较的是ordinal值
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;//根据ordinal值比较大小
}
//返回与此枚举常量的枚举类型相对应的 Class 对象
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
//获取class对象引用,getClass()是Object的方法
Class<?> clazz = getClass();
//获取父类Class对象引用
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) {
//enumType.enumConstantDirectory()获取到的是一个map集合,key值就是name值,value则是枚举变量值
//enumConstantDirectory是class对象内部的方法,根据class对象获取一个map集合的值
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);
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
return super.hashCode();
}
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
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");
}
}
下面例子测试其方法:
public class MyTest {
public static void main(String[] args) {
Date[] dates = {Date.ONE, Date.TWO, Date.THREE, Date.FOUR, Date.FIVE};
for (Date date : dates) {
System.out.println("name:" + date.name() + " ordinal:" + date.ordinal() + " class:" + date.getDeclaringClass());
}
System.out.println("-----------------------------");
System.out.println("dates[0].compareTo(dates[1]):" + dates[0].compareTo(dates[1]));
System.out.println("-----------------------------");
Date d1 = Date.valueOf(Date.class, dates[0].name());
Date d2 = Enum.valueOf(Date.class, dates[0].name());
System.out.println("d1:" + d1 + " d2:" + d2);
}
}
运行输出:
name:ONE ordinal0 class:class Date
name:TWO ordinal1 class:class Date
name:THREE ordinal2 class:class Date
name:FOUR ordinal3 class:class Date
name:FIVE ordinal4 class:class Date
-----------------------------
dates[0].compareTo(dates[1])-1
-----------------------------
d1:ONE d2:ONE
Values()方法与ValueOf()方法
values()方法和valueOf(String name)方法是编译器生成的static方法,在Enum类中并没出现values()方法,但valueOf()方法还是有出现的,只不过编译器生成的valueOf()方法需传递一个name参数,而Enum自带的静态方法valueOf()则需要传递两个方法,其实,编译器生成的valueOf方法最终还是调用了Enum类的valueOf方法:
public class MyTest {
public static void main(String[] args) {
System.out.println(Arrays.toString(Date.values()));
System.out.println(Date.valueOf("ONE"));
}
}
运行输出:
[ONE, TWO, THREE, FOUR, FIVE]
ONE
values()方法的作用就是获取枚举类中的所有变量,并作为数组返回。
valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量。
三:枚举类使用
- enum 与 class、interface 具有相同地位,但是不能继承与被继承,也不能创建实例,只能由JVM去创建。
- 可以实现多个接口,也可以拥有构造器、成员方法、成员变量。
实现接口
public interface ClickListener {
void onClick();
}
public enum Date implements ClickListener{
ONE, TWO, THREE, FOUR, FIVE;
@Override
public void onClick() {
}
}
定义构造函数
注意:只能是私有的构造函数或默认
public enum Date{
ONE, TWO, THREE, FOUR, FIVE;
private Date(){}
}
定义成员方法和成员变量
public enum Date{
ONE, TWO, THREE, FOUR, FIVE;
private String a;
public String getContext(){
return Arrays.toString(values());
}
}
EnumMap
java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumMap继承了AbstractMap类,因此EnumMap具备一般map的使用方法,主要通过一个保存key的数组K[] keyUniverse和一个保存value的数组Object[] vals来操作。
源码:
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable
{
//Class对象引用
private final Class<K> keyType;
//存储Key值的数组
private transient K[] keyUniverse;
//存储Value值的数组
private transient Object[] vals;
//map的size
private transient int size = 0;
//空map
private static final Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0];
//构造函数
public EnumMap(Class<K> keyType) {
this.keyType = keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
//返回枚举数组
private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
//最终调用到枚举类型的values方法,values方法返回所有可能的枚举值
return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(keyType);
}
public V put(K key, V value) {
typeCheck(key);//检测key的类型
//获取存放value值得数组下标
int index = key.ordinal();
//获取旧值
Object oldValue = vals[index];
//设置value值
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);//返回旧值
}
private void typeCheck(K key) {
Class<?> keyClass = key.getClass();//获取类型信息
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
//代表NULL值得空对象实例
private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "java.util.EnumMap.NULL";
}
};
private Object maskNull(Object value) {
//如果值为空,返回NULL对象,否则返回value
return (value == null ? NULL : value);
}
@SuppressWarnings("unchecked")
private V unmaskNull(Object value) {
//将NULL对象转换为null值
return (V)(value == NULL ? null : value);
}
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
//对Key值的有效性和类型信息进行判断
private boolean isValidKey(Object key) {
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class<?> keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
public V remove(Object key) {
//判断key值是否有效
if (!isValidKey(key))
return null;
//直接获取索引
int index = ((Enum<?>)key).ordinal();
Object oldValue = vals[index];
//对应下标元素值设置为null
vals[index] = null;
if (oldValue != null)
size--;//减size
return unmaskNull(oldValue);
}
//判断是否包含某value
public boolean containsValue(Object value) {
value = maskNull(value);
//遍历数组实现
for (Object val : vals)
if (value.equals(val))
return true;
return false;
}
//判断是否包含key
public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null;
}
}
下面例子测试其方法:
public class MyTest {
public static void main(String[] args) {
EnumMap enumMap=new EnumMap(Date.class);
//添加元素
enumMap.put(Date.ONE,1);
enumMap.put(Date.TWO,2);
enumMap.put(Date.THREE,3);
//删除元素
enumMap.remove(Date.TWO);
//遍历输出
System.out.println(enumMap.toString());
}
}
运行输出:
{ONE=1, THREE=3}
EnumSet
EnumSet是与枚举类型一起使用的专用 Set 集合,EnumSet 中所有元素都必须是枚举类型。注意EnumSet不允许使用 null 元素。试图插入 null 元素将抛出 NullPointerException,但试图测试判断是否存在null 元素或移除 null 元素则不会抛出异常,与大多数collection 实现一样,EnumSet不是线程安全的,因此在多线程环境下应该注意数据同步问题。
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
//元素的class
final Class<E> elementType;
final Enum<?>[] universe;
private static Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0];
EnumSet(Class<E>elementType, Enum<?>[] universe) {
this.elementType = elementType;
this.universe = universe;
}
//创建一个具有指定元素类型的空EnumSet。
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
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
EnumSet<E> result = noneOf(elementType);
result.addAll();
return result;
}
abstract void addAll();
创建一个包含参数容器中的所有元素的EnumSet
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) {
return s.clone();
}
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
if (c instanceof EnumSet) {
return ((EnumSet<E>)c).clone();
} else {
if (c.isEmpty())
throw new IllegalArgumentException("Collection is empty");
Iterator<E> i = c.iterator();
E first = i.next();
EnumSet<E> result = EnumSet.of(first);
while (i.hasNext())
result.add(i.next());
return result;
}
}
// 初始集合包括指定集合的补集
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
EnumSet<E> result = copyOf(s);
result.complement();
return result;
}
// 创建一个包括参数中所有元素的EnumSet
public static <E extends Enum<E>> EnumSet<E> of(E e) {
EnumSet<E> result = noneOf(e.getDeclaringClass());
result.add(e);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) {
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
result.add(e3);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) {
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
result.add(e3);
result.add(e4);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4,
E e5)
{
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
result.add(e3);
result.add(e4);
result.add(e5);
return result;
}
@SafeVarargs
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
EnumSet<E> result = noneOf(first.getDeclaringClass());
result.add(first);
for (E e : rest)
result.add(e);
return result;
}
// 创建一个包括枚举值中指定范围元素的EnumSet
public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
if (from.compareTo(to) > 0)
throw new IllegalArgumentException(from + " > " + to);
EnumSet<E> result = noneOf(from.getDeclaringClass());
result.addRange(from, to);
return result;
}
abstract void addRange(E from, E to);
@SuppressWarnings("unchecked")
public EnumSet<E> clone() {
try {
return (EnumSet<E>) super.clone();
} catch(CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
abstract void complement();
final void typeCheck(E e) {
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
throw new ClassCastException(eClass + " != " + elementType);
}
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
return SharedSecrets.getJavaLangAccess()
.getEnumConstantsShared(elementType);
}
}
下面例子测试其方法:
创建EnumSet并不能使用new关键字,因为它是个抽象类,而应该使用其提供的静态工厂方法,EnumSet的静态工厂方法比较多,如下:
public class MyTest {
public static void main(String[] args) {
EnumSet<Date> enumSet= EnumSet.noneOf(Date.class);
enumSet.add(Date.ONE);
enumSet.add(Date.TWO);
enumSet.add(Date.THREE);
System.out.println("添加后:"+enumSet.toString());
EnumSet<Date> enumSet1= EnumSet.allOf(Date.class);
System.out.println("add后:"+enumSet1.toString());
//初始集合包括枚举值中指定范围的元素
EnumSet<Date> enumSet2= EnumSet.range(Date.ONE,Date.THREE);
System.out.println("指定范围:"+enumSet2.toString());
//指定补集,也就是从全部枚举类型中去除参数集合中的元素,如下去掉上述enumSet2的元素
EnumSet<Date> enumSet3= EnumSet.complementOf(enumSet2);
System.out.println("指定补集:"+enumSet3.toString());
//初始化时直接指定元素
EnumSet<Date> enumSet4= EnumSet.of(Date.ONE);
System.out.println("指定Date.ONE元素:"+enumSet4.toString());
EnumSet<Date> enumSet5= EnumSet.of(Date.ONE,Date.FOUR);
System.out.println("指定Color.BLACK和Color.GREEN元素:"+enumSet5.toString());
//复制enumSet5容器的数据作为初始化数据
EnumSet<Date> enumSet6= EnumSet.copyOf(enumSet5);
System.out.println("enumSet6:"+enumSet6.toString());
List<Date> list = new ArrayList<Date>();
list.add(Date.ONE);
list.add(Date.ONE);//重复元素
list.add(Date.TWO);
list.add(Date.THREE);
System.out.println("list:"+list.toString());
//使用copyOf(Collection<E> c)
EnumSet enumSet7=EnumSet.copyOf(list);
System.out.println("enumSet7:"+enumSet7.toString());
}
}
运行输出:
添加后:[ONE, TWO, THREE]
add后:[ONE, TWO, THREE, FOUR, FIVE]
指定范围:[ONE, TWO, THREE]
指定补集:[FOUR, FIVE]
指定Date.ONE元素:[ONE]
指定Color.BLACK和Color.GREEN元素:[ONE, FOUR]
enumSet6:[ONE, FOUR]
list:[ONE, ONE, TWO, THREE]
enumSet7:[ONE, TWO, THREE]