java中的枚举类型

概述

枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。

枚举的定义

//使用普通方式定义常量
public class DayDemo {
    public static final int MONDAY =1;
    public static final int TUESDAY=2;
    public static final int WEDNESDAY=3;
    public static final int THURSDAY=4;
    public static final int FRIDAY=5;
    public static final int SATURDAY=6;
    public static final int SUNDAY=7;
}

这种定义方式存在许多不足,如在类型安全和使用方便性上并没有多少好处,如果存在定义相同int值的变量,混淆的几率还是很大的,编译器也不会提出任何警告,因此这种方式在枚举出现后并不提倡。

//使用枚举类型定义
public class EnumDemo {

    public static void main(String[] args){
        //直接引用
        Day day =Day.MONDAY;
    }

}
//枚举类型,使用关键字enum
enum Day {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们。

注意:

  1. 枚举的值一般是大写的字母,多个值之间以逗号分隔
  2. 枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天

枚举实现原理

编译之前:
在这里插入图片描述
编译之后:
在这里插入图片描述
可以看到生成了两个.class文件。实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。

反编译Day.class文件

package Enum;

final class Day extends Enum
{

    public static Day[] values()
    {
        return (Day[])$VALUES.clone();
    }

    public static Day valueOf(String s)
    {
        return (Day)Enum.valueOf(Enum/Day, s);
    }

    private Day(String s, int i)
    {
        super(s, i);
    }

    public static final Day MONDAY;
    public static final Day TUESDAY;
    public static final Day WEDNESDAY;
    public static final Day THURSDAY;
    public static final Day FRIDAY;
    public static final Day SATURDAY;
    public static final Day SUNDAY;
    private static final Day $VALUES[];

    static 
    {
        MONDAY = new Day("MONDAY", 0);
        TUESDAY = new Day("TUESDAY", 1);
        WEDNESDAY = new Day("WEDNESDAY", 2);
        THURSDAY = new Day("THURSDAY", 3);
        FRIDAY = new Day("FRIDAY", 4);
        SATURDAY = new Day("SATURDAY", 5);
        SUNDAY = new Day("SUNDAY", 6);
        $VALUES = (new Day[] {
            MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
        });
    }
}

  1. 编译器帮助我们生成了一个Day类(该类是final类型无法被继承)而且该类继承自java.lang.Enum类
  2. 编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了我们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过代表的内容不一样而已。
  3. 编译器还为我们生成了两个静态方法,分别是values()和 valueOf()

枚举的常见方法

Enum
//实现了Comparable
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;//枚举顺序值
   //该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始
    public final int ordinal() {
        return ordinal;
    }

    //枚举的构造方法,只能由编译器调用,我们只能使用enum关键字定义枚举
    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;
    }

    //比较枚举的大小,比较的是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值比较大小
    }

    @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);
    }

}

enumConstantDirectory方法源码:

Map<String, T> enumConstantDirectory() {
        if (enumConstantDirectory == null) {
            //getEnumConstantsShared最终通过反射调用枚举类的values方法
            T[] universe = getEnumConstantsShared();
            if (universe == null)
                throw new IllegalArgumentException(
                    getName() + " is not an enum type");
            Map<String, T> m = new HashMap<>(2 * universe.length);
            //map存放了当前enum类的所有枚举实例变量,以name为key值
            for (T constant : universe)
                m.put(((Enum<?>)constant).name(), constant);
            enumConstantDirectory = m;
        }
        return enumConstantDirectory;
    }
    private volatile transient Map<String, T> enumConstantDirectory = null;

实际上通过调用enumType(Class对象的引用)的enumConstantDirectory方法获取到的是一个Map集合,在该集合中存放了以枚举name为key和以枚举实例变量为value的Key&Value数据,因此通过name的值就可以获取到枚举实例。

编译器生成的Values方法与ValueOf方法

Enum:有valueOf(Class enumType,String name)方法,需传递两个参数,无values()方法。
Enum的子类:有valueOf(String name)方法,需传递一个参数,有values()方法。
values()方法和valueOf(String name)方法是编译器生成的static方法,从前面反编译后的代码可以看出,编译器生成的valueOf方法最终还是调用了Enum类的valueOf方法

Day[] days2 = Day.values();
System.out.println("day2:"+Arrays.toString(days2));
Day day = Day.valueOf("MONDAY");
System.out.println("day:"+day);

输出结果:
day2:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
day:MONDAY

从结果可知道,values()方法的作用就是获取枚举类中的所有变量,并作为数组返回,而valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量,只不过编译器生成的valueOf方法更简洁些只需传递一个参数。

注意:
由于values()方法是由编译器插入到枚举类中的static方法,所以如果我们将枚举实例向上转型为Enum,那么values()方法将无法被调用,因为Enum类中并没有values()方法,valueOf(String name)方法也是同样的道理

枚举的Class对象
//Class对象有关枚举的两个方法:

//返回该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。
 T[] getEnumConstants() 

//当且仅当该类声明为源代码中的枚举时返回 true
boolean	isEnum()	
//正常使用
Day[] ds=Day.values();
//向上转型Enum
Enum e = Day.MONDAY;
//无法调用,没有此方法
//e.values();
//获取class对象引用
Class c = e.getDeclaringClass();
if(c.isEnum()) {
    Day[] d = (Day[]) c.getEnumConstants();
    System.out.println(Arrays.toString(d));
}

输出结果:
[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]

由此可见,通过Enum的class对象的getEnumConstants方法,我们可以获取所有的枚举实例常量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值