一、什么是枚举
枚举是我们自己定义的一个集合,当我们要使用这个集合的时候,只能使用集合里的元素,不是这个集合里的元素我们就不可以使用。
二、枚举可以干什么
枚举的出现主要是为了代替静态常量,
常量有什么弊端
1.常量必须定义类型,但是如果定义了类型就不明确它定义的意义。
2.命名局限性,有时候我们会忘记自己定义的变量是干什么,例如:
private static final int MEN=0;
private static final int WOMEN=1;
这些变量完全可以做加减运算,但是这可能不是我们的本意,我们就仅仅想让他们表示性别,字面上的意思。当我们输出的时候,我们想让他输出的是MEN,但是用静态变量只能输出0.如果忘记了0所代表的含义,有时候不得不翻翻0所代表的含义。这样是没有可读性的
枚举的应用场景
1.作为普通的常量使用,如四季,星期,颜色
2.对于有穷个对象集合,可以配合switch使用
3.当我们可能会用到很多的静态常量的时候,如果全写在一个文件里,容易造成命名混淆,用枚举就可以就解决这个问题。
4.枚举不仅仅能代表常量使用,还可以添加自己的方法。
三,枚举的底层原理
所有的enum默认继承自Enum,看一下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;
}
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();
}
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;
}
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);
}
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");
}
protected final void finalize() { }
}
在源代码中,可以得出以下几个信息
1.Enum有两个成员变量:name和ordinal。其中,name用于记录枚举常量的名字,ordinal用于记录枚举常量在声明时的顺序,从0开始计数。
2.Enum类有一个构造器,分别为name和ordinal赋值
3.Enum重写了toString方法,返回枚举常量的name
4.Enum有equals方法,直接用==比较
5.Enum实现了Comparable接口,直接比较枚举常量ordinal的值。
6.Enum有一个valueOf方法,根据枚举类型以及name返回一个枚举常量。
7.Enum不允许克隆,直接抛出异常
8.Enum不允许反序列化,用于保证枚举永远是单例的。
自己定义一个枚举类
public enum Week {
MONDAY(0),TUESDAY(1),WEDNESDAY(2),
THURSDAY(3),FRIDAY(4),SATURDAY(5),SUNDAY(6);
int num;
Week(int num){
this.num=num;
}
}
我们自己定义的枚举类实际上在底层是这样的结构:
public final class Week extends Enum{
private static final Week MONDAY;
private static final Week TUESDAY;
private static final Week WEDNESDAY;
private static final Week THURSDAY;
private static final Week FRIDAY;
private static final Week SATURDAY;
private static final Week SUNDAY;
int num;
private static final Week $VALUES[];
static {
MONDAY=new Week("MONDAY",0,1);
TUESDAY=new Week("TUESDAY",1,2);
WEDNESDAY=new Week("WEDNESDAY",2,3);
THURSDAY=new Week("THURSDAY",3,4);
FRIDAY=new Week("FRIDAY",4,5);
SATURDAY=new Week("SATURDAY",5,6);
SUNDAY=new Week(" SUNDAY",6,7);
$VALUES=(new Week[]{MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY});
}
private Week(String s,int i,int j){
super(s,i);
num=j;
}
public static Week[] values(){
return (Week[])$VALUES.clone();
}
public static Week valueOf(String s){
return (Week) Enum.valueOf(Week,s);
}
}
四,使用枚举需要注意的点。
1.枚举不允许继承类,JVM在生成枚举时已经继承了Enum类,由于java是单继承语言,所以不允许在继承额外的类。
2.枚举允许实现接口,因为枚举本身就是一个类,类可以实现多个接口
3.枚举可以用==
比较,JVM会为每一个枚举实例生成一个类对象,这个类对象是用public static final修饰的,在static代码块中初始化,是一个单例。
4.不可以继承枚举,因为所有枚举默认是final的。
5.枚举本身就是一种对单例模式友好的,它是实现单例模式的一种很好方法。
6.枚举的compareTo()方法比较的是ordinal的值
7.枚举的equals()比较的是枚举对象的内存地址,作用与==
等价