• Java基础—枚举


    定义

      枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示。

    为什么要用枚举

      在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量。之前我们通常利用public final static 方法定义的代码如下。

    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枚举模式。可这种模式有什么问题呢,我们都用了那么久了,应该没问题的。通常我们写出来的代码都会考虑它的安全性、易用性和可读性。 首先我们来考虑一下它的类型安全性。当然这种模式不是类型安全的。比如说我们设计一个函数,要求传入某个值。但是使用int类型,我们无法保证传入的值为合法,编译器也不会提出任何警告,因此这种方式在枚举出现后并不提倡。

      现在我们利用枚举类型来重新定义上述的常量,同时也感受一把枚举定义的方式,如下定义周一到周日的常量

    public class EnumDemo {
        public static void main(String[] args){
            //直接引用
            Day day =Day.MONDAY;
        }
    }
    //定义枚举类型
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }

      相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。

    枚举的实现原理

      我们大概了解了枚举类型的定义与简单使用后,现在有必要来了解一下枚举类型的基本实现原理。实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。

      我们来看看反编译Day.class文件:

    //反编译Day.class
    final class Day extends Enum
    {
        //编译器为我们添加的静态的values()方法
        public static Day[] values()
        {
            return (Day[])$VALUES.clone();
        }
        //编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
        public static Day valueOf(String s)
        {
            return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
        }
        //私有构造函数
        private Day(String s, int i)
        {
            super(s, i);
        }
         //前面定义的7种枚举实例
        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
            });
        }
    }

      从反编译的代码可以看出编译器确实帮助我们生成了一个Day类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类,该类是一个抽象类(稍后我们会分析该类中的主要方法),除此之外,编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了我们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过代表的内容不一样而已。

      到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;

      同时编译器会为该类创建两个方法,分别是values()和valueOf()。ok~,到此相信我们对枚举的实现原理也比较清晰,下面我们深入了解一下java.lang.Enum类以及values()和valueOf()的用途。

    枚举类常用方法

      Enum是所有 Java 语言枚举类型的公共基本类(注意Enum是抽象类),以下是它的常见方法:

    返回类型方法名称方法说明
    int compareTo(E o) 比较此枚举与指定对象的顺序
    boolean equals(Object other) 当指定对象等于此枚举常量时,返回 true。
    Class<?> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象
    String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明
    int ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
    String toString() 返回枚举常量的名称,它包含在声明中
    static<T extends Enum<T>> T static valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。

      这里主要说明一下ordinal()方法,该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,如果MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。compareTo(E o)方法则是比较枚举的大小,注意其内部实现是根据每个枚举的ordinal值大小进行比较的。name()方法与toString()几乎是等同的,都是输出变量的字符串形式。至于valueOf(Class<T> enumType, String name)方法则是根据枚举类的Class对象和枚举名称获取枚举常量,注意该方法是静态的,下面的代码演示了上述方法:

    package com.zejian.enumdemo;
    
    public class EnumDemo {
    
        public static void main(String[] args){
    
            //创建枚举数组
            Day[] days=new Day[]{Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY,
                    Day.THURSDAY, Day.FRIDAY, Day.SATURDAY, Day.SUNDAY};
    
            for (int i = 0; i <days.length ; i++) {
                System.out.println("day["+i+"].ordinal():"+days[i].ordinal());
            }
    
            System.out.println("-------------------------------------");
            //通过compareTo方法比较,实际上其内部是通过ordinal()值比较的
            System.out.println("days[0].compareTo(days[1]):"+days[0].compareTo(days[1]));
            System.out.println("days[0].compareTo(days[1]):"+days[0].compareTo(days[2]));
    
            //获取该枚举对象的Class对象引用,当然也可以通过getClass方法
            Class<?> clazz = days[0].getDeclaringClass();
            System.out.println("clazz:"+clazz);
    
            System.out.println("-------------------------------------");
    
            //name()
            System.out.println("days[0].name():"+days[0].name());
            System.out.println("days[1].name():"+days[1].name());
            System.out.println("days[2].name():"+days[2].name());
            System.out.println("days[3].name():"+days[3].name());
    
            System.out.println("-------------------------------------");
    
            System.out.println("days[0].toString():"+days[0].toString());
            System.out.println("days[1].toString():"+days[1].toString());
            System.out.println("days[2].toString():"+days[2].toString());
            System.out.println("days[3].toString():"+days[3].toString());
    
            System.out.println("-------------------------------------");
    
            Day d=Enum.valueOf(Day.class,days[0].name());
            Day d2=Day.valueOf(Day.class,days[0].name());
            System.out.println("d:"+d);
            System.out.println("d2:"+d2);
        }
    
    }
    enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

    运行结果:

      day[0].ordinal():0
       day[1].ordinal():1
       day[2].ordinal():2
       day[3].ordinal():3
       day[4].ordinal():4
       day[5].ordinal():5
       day[6].ordinal():6
       -------------------------------------
       days[0].compareTo(days[1]):-1
       days[0].compareTo(days[1]):-2
       clazz:class com.zejian.enumdemo.Day
       -------------------------------------
       days[0].name():MONDAY
       days[1].name():TUESDAY
       days[2].name():WEDNESDAY
       days[3].name():THURSDAY
       -------------------------------------
       days[0].toString():MONDAY
       days[1].toString():TUESDAY
       days[2].toString():WEDNESDAY
       days[3].toString():THURSDAY
       -------------------------------------
       d:MONDAY
       d2:MONDAY

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

      values()方法和valueOf(String name)方法是编译器生成的static方法,下面通过代码来演示这两个方法的作用:

    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()方法也是同样的道理,注意是一个参数的。

     //正常使用
    Day[] ds=Day.values();
    //向上转型Enum
    Enum e = Day.MONDAY;
    //无法调用,没有此方法
    //e.values();

    枚举类的基本使用

    常量的使用 

      可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

    package com;
     
    public enum Color {     
         RED, GREEN, BLANK, YELLOW  
    }
    package com;
     
    public class B { 
        public static void main(String[] args) { 
            System.out.println( isRed( Color.BLANK ) ) ;  //结果: false
            System.out.println( isRed( Color.RED ) ) ;    //结果: true
     
        }
     
     
        static boolean isRed( Color color ){
            if ( Color.RED.equals( color )) {
                return true ;
            }
            return false ;
        } 
    }

    switch

    package com;
     
    public class B { 
        public static void main(String[] args) { 
            showColor( Color.RED ); 
        }
     
        static void showColor(Color color){
            switch ( color ) {
            case BLANK:
                System.out.println( color );
                break;
            case RED :
                System.out.println( color );
                break;
            default:
                System.out.println( color );
                break;
            }         
        }
    }

    自定义函数

    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 static String getName(int index) {
            for (Color c : Color.values()) {
            if (c.getIndex() == index) {
                return c.name;
            }
            }
            return null;
        }
    
        // get set 方法
        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;
        }
        }
    package com;
     
    public class B { 
        public static void main(String[] args) {
     
            //输出某一枚举的值
            System.out.println( Color.RED.getName() );
            System.out.println( Color.RED.getIndex() );
     
            //遍历所有的枚举
            for( Color color : Color.values()){
                System.out.println( color + "  name: " + color.getName() + "  index: " + color.getIndex() );
            }
        }
     
    }

    输出结果:

    红色
    1
    RED name: 红色 index: 1
    GREEN name: 绿色 index: 2
    BLANK name: 白色 index: 3
    YELLO name: 黄色 index: 4

     

     

     

    参考:

    Java 枚举类的基本使用

    java enum(枚举)使用详解 + 总结

    深入理解Java枚举类型(enum)

    Java的枚举类型使用方法详解

  • 相关阅读:
    【Python数据分析】NumPy之数组对象基础
    【Oracle11g】20_函数
    【Word】排版技巧
    cache介绍
    cache verilog实现
    在verilog中使用格雷码
    同步fifo与异步fifo
    AHB总线协议(二)
    Android Handler 消息机制原理解析
    值得推荐的开源C/C++框架和库
  • 原文地址:https://www.cnblogs.com/Jason-Xiang/p/8522959.html
Copyright © 2020-2023  润新知