• 浅谈 Java 中的枚举


    枚举也就是一一列举,常用来表示那些可以明确范围的集合,比方说性别,季节,星期,月份等。

    在 JDK 1.5 才出现枚举类,在没有出现枚举类之前,我们要表示几个确定的值通常会使用常量来表示,形如

        public static final Integer SPRING = 1;
        public static final Integer SUMMER = 2;
        public static final Integer FALL = 3;
        public static final Integer WINTER = 4;

    我们可以使用枚举类来表示,这也就是最简单的枚举。

    enum Season{
        SPRING,SUMMER,FALL,WINTER;
    }

    那么枚举类和定义常量相比有什么优势呢?

    安全,我们看上面的代码可以知道,使用常量表示的季节都是 Integer 类型的,而这个类型的数据范围太大了,而使用枚举就限制了数据的域。枚举可以理解为几个常量的集合,数据不太会改变,使用枚举之后语义更加明确,因为数据域不大。

    关于枚举类,有几点需要注意:

    1. enum 和 class ,interface 的地位一样

    2. 使用 enum 定义的枚举类默认继承了 java.lang.Enum,而不是继承 Object 类。枚举类可以实现一个或多个接口,不能再继承其他类了。

    3. 枚举类的所有实例都必须放在第一行展示,不需使用 new 关键字,不需显式调用构造器。自动添加 public static final 修饰。

    4. 枚举类的构造器只能是私有的。

    关于第 4 点,我要说枚举类的定义是单例模式的,单例模式要求构造器私有化,不允许在外部创建新的对象,你想呀,枚举类的作用就是准确的表示出同一类别下的不同数据。在我们定义的时候已经创建好了,所以跟本不需要也不能在外部继续创建新的对象。故,需要将构造器私有化。

    枚举类说的通俗点就是被阉割的类,我们使用类可以创建对象,而枚举类在定义的时候就会指定创建的对象有哪些。你没有显示的写代码创建,不过是由于 JVM 自动给你加上去了。待会看看枚举类被反编译之后的情况就会理解。

    枚举类中可以定义属性和方法。

    enum Season{
    
        SPRING("春天","一年之计在于春"),SUMMER("夏天","夏天好热 ~ "),
        FALL("秋天","秋水共长天一色"),WINTER("冬天","冬天好冷 ~ ");
    
        // 添加枚举对象的名称
        private final String name;
    
        // 添加枚举对象的描述
        private final String desc;
    
        private Season(String name,String desc){
            this.name = name;
            this.desc = desc;
        }
    
        public String getName(){
            return name;
        }
    
        public String getDesc(){
            return desc;
        }
    }

    我们可以看到,有一个带参的私有构造函数,所以在定义枚举的时候可以直接赋值,而调用创建对象是由虚拟机自动完成的,还可以添加更多的属性,我们可以正常使用枚举对象。

    // 获取所有的枚举对象,返回的是数组
    Season[] values = Season.values();
    for (Season season : values) {
    System.out.println(season.getName() + " : " + season.getDesc());
    }
    
    春天 : 一年之计在于春
    夏天 : 夏天好热 ~ 
    秋天 : 秋水共长天一色
    冬天 : 冬天好冷 ~

    枚举类中实现抽象方法

    这肯定有点迷糊,我们知道含有抽象方法的类是抽象类,那为什么枚举类不是 abstract 的也可以含有抽象方法呢 ?我们先来看看实现,待会说原因。

    enum Season{
    
        SPRING("春天","一年之计在于春") {
            @Override
            public Season getNextSeason() {
                return Season.valueOf("SUMMER");
            }
        },SUMMER("夏天","夏天好热 ~ ") {
            @Override
            public Season getNextSeason() {
                return Season.valueOf("FALL");
            }
        },
            ... 省略
            ... 省略
            ... 省略
    
        // 定义了一个抽象方法,获取下一个季节
        public abstract Season getNextSeason();
    }
    
    

    测试代码如下

    public static void main(String[] args) {
        String name = Season.SPRING.getNextSeason().getName();
        System.out.println(name); // 夏天
    }

    是不是有种似懂非懂的感觉,没关系,我们来反编译看一看枚举类的内部构造你就会明白。

    举个简单的例子来看一看枚举类的原理。就拿一个最简单的枚举来说明情况。

    public enum SeasonEnum {
        SPRING,SUMMER,FALL,WINTER;
    }

    看一看反编译之后的代码,我使用的反编译工具是 jad 。

    public final class SeasonEnum extends Enum
    {
    
        public static SeasonEnum[] values()
        {
            return (SeasonEnum[])$VALUES.clone();
        }
    
        public static SeasonEnum valueOf(String s)
        {
            return (SeasonEnum)Enum.valueOf(SeasonEnum, s);
        }
    
        private SeasonEnum(String s, int i)
        {
            super(s, i);
        }
    
        public static final SeasonEnum SPRING;
        public static final SeasonEnum SUMMER;
        public static final SeasonEnum FALL;
        public static final SeasonEnum WINTER;
        private static final SeasonEnum $VALUES[];
    
        static 
        {
            SPRING = new SeasonEnum("SPRING", 0);
            SUMMER = new SeasonEnum("SUMMER", 1);
            FALL = new SeasonEnum("FALL", 2);
            WINTER = new SeasonEnum("WINTER", 3);
            $VALUES = (new SeasonEnum[] {
                SPRING, SUMMER, FALL, WINTER
            });
        }
    }

    可以看到枚举类本质上就是一个继承了 Enum 的一个单例最终类,而我们定义的枚举对象也是在静态代码块中初始化了,同时我们也可以看到 values 方法的实现就是返回实例对象的数组,也就是枚举对象的数组,valueOf 方法接收一个字符串,返回的是字符串对应的枚举对象,若是找不到,会报错。

    看到这我们也就能理解为什么我们可以在枚举类中定义抽象方法了,我们使用内部类实现了抽象方法,对应的每个对象在创建时都会实现相应的抽象方法。同样的道理,枚举类也可以实现接口,这里就不演示了。

    说了这么多,也知道了枚举的好处,但是不得不说的是我在实际的项目中,枚举类的使用还真不多,下面就看看枚举类可以有哪些用处,以后有这种需求就可以考虑使用枚举来实现。

    switch 语句支持枚举类型是从 JDK 1.5 才开始的,因为此时才有枚举这个特性。我们看一看具体的使用。

    /**
     * 服务器响应码
     */
    public enum ResponseCode {
    
        SUCCESS(200,"访问成功"),FAIL(404,"页面不存在"),ERROR(500,"服务器内部错误");
    
        private Integer num;
        private String desc;
    
        private ResponseCode(Integer num,String desc){
            this.num = num;
            this.desc = desc;
        }
    
        public Integer getNum() {
            return num;
        }
    
        public String getDesc() {
            return desc;
        }
    
        /*
         * 通过返回码得到枚举对象
         */
        public static ResponseCode getByNum(Integer num){
            for(ResponseCode code : values()){
                if(code.getNum().equals(num)){
                    return code;
                }
            }
            return null;
        }
    }
    =============测试=====================
        public static void main(String[] args) {
            ResponseCode code = ResponseCode.getByNum(200);
            switch (code) {
            case SUCCESS:
                System.out.println("成功");
                break;
    
            default:
                break;
            }
        }

    在我做过的项目中,只有一处用到过枚举,用来记录字典的值,比方说,我写一个工具类,记录项目中使用到的所有字典相关数据。形如这样

    public class Keys {
        enum Sex{
            MALE,FEMALE;
        }
        enum State{
            SUCCESS,FAIL;
        }
        enum Month{ 
        }
        enum Week{      
        }
    
        public static void main(String[] args) {
            Keys.Sex.MALE.name();
        }
    }

    但是在后面发现,还有更好用的配置字典的方式,创建一个字典类,将所有的字典数据放在一张表中,我们使用字典类来操作,这样要比上面使用枚举要好。

  • 相关阅读:
    C connect实现Timeout效果(Linux)
    QSS网址
    C实现读写文件
    crond守护进程实现定时监控某进程占有内存的大小
    Ubuntu17安装Chrome有效
    Ubuntu16安装wine(转)
    直方图均衡化
    函数后面的const修饰符的作用
    C 线程学习记录
    Override Fuction 调用——到底使用的是谁的函数
  • 原文地址:https://www.cnblogs.com/YJK923/p/9597022.html
Copyright © 2020-2023  润新知