写点什么

清华教授,手把手带你深度解读 Java 枚举,理论与实战齐飞

发布于: 1 小时前
清华教授,手把手带你深度解读Java枚举,理论与实战齐飞

今日分享开始啦,请大家多多指教~


今日主要分享的是 Java 枚举,被 enum 关键字修饰的类型就是枚举类型,如果枚举不添加任何方法,枚举值默认为从 0 开始的有序数值。例如:enum Color{RED,GREEN,BLUE};将常量组织起来,统一进行管理。应用的场景为错误码、状态机等;枚举的本质是 java.lang.Enum 的子类。


一、基本概念


枚举是 Java1.5 引入的新特性,通过关键字 enum 来定义枚举类。枚举类是一种特殊类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口,但枚举类不能继承其他类。


二、枚举的优缺点


1、优点


Effctive Java 中之所以推荐用枚举代替所有常量 Code,原因如下:


(1)类型检查,有效性检查


(2)枚举作为一个类,可以有自己的属性(通常应该是常量,我没遇到过不是的情况)以及自己的方法(否则只能用 switch 来写,实际违反原则)


(3)和常量相比,无需查看文档和源码就能直接知道所有可能返回值,方便编码。


然而这里的问题就出在第一点上,实际上分布式环境下(1)并不是必然的。如果业务处理中允许某个接口返回值有未定义内容,那么在反序列化中就不该对此抛出异常,不必死守(1)。同时,从第(2)点和第(3)点来看,这样限制枚举的使用范围造成的影响是极大的。将有自己属性,自己方法实现的枚举改写为 code 和其他方法的配合,需要的代码量上升不少,而且代码腐烂度极大增加。


2、缺点


(1)由于 Java 中支持单继承,因此枚举类型不能再继承其他类;


(2)使用枚举作为返回值可能造成的问题其实大家都知道就是客户端和服务端版本不一致的话,会造成反序列化异常,于是《阿里巴巴 JAVA 开发手册》对于这个问题的处理办法就采取了尽量避免异常出现,所以禁止定义枚举为返回值。


三、解决 ifelse


对于业务开发来说,业务逻辑的复杂是必然的,随着业务发展,需求只会越来越复杂,为了考虑到各种各样的情况,代码中不可避免的会出现很多 if-else。


一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性,而且代码显得很 low。


枚举可以解决这个问题;


关于枚举与 switch 是个比较简单的话题,使用 switch 进行条件判断时,条件参数一般只能是整型,字符型。而枚举型确实也被 switch 所支持,在 java 1.7 后 switch 也对字符串进行了支持。这里我们简单看一下 switch 与枚举类型的使用



四、枚举的常用方法


1、枚举类


2、枚举类变量中添加属性


3、测试类


public static void main(String[] args) {        //1、ordinal(),枚举顺序值        System.out.println("枚举顺序值,"+Weekday.MONDAY.ordinal());//1        /*         * 2、valueOf()         * public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)         * enumType -- 这是枚举类型,返回一个常量的类的对象。         * name -- 这是常量,要返回的名称。         * return:此方法返回具有指定名称的枚举类型的枚举常量。         * 如果你传了一个不存在的字符串,那么会抛出异常。         * */        Week week = Enum.valueOf(Week.class,Week.MONDAY.name().toString());        Week week1 = Week.valueOf(Week.class,Week.MONDAY.name());        System.out.println("Enum.valueOf,"+week);//MONDAY        System.out.println("Week.valueOf,"+week1);//MONDAY        //3、values()        System.out.println("Week.values(),"+Weekday.values());       //返回一个Weekday数组,[Ljavase.enumeration.Weekday;@2a84aee7        //4、通过compareTo方法比较,实际上其内部是通过ordinal()值比较的        System.out.println("Weekday.MONDAY.compareTo(Weekday.TUESDAY),"+Weekday.MONDAY.compareTo(Weekday.TUESDAY));       //false         //5、获取该枚举对象的Class对象引用,当然也可以通过getClass方法        Class<?> declaringClass = Weekday.MONDAY.getDeclaringClass();        System.out.println("获取该枚举对象的Class对象引用,"+declaringClass);       //javase.enumeration.Weekday         //6、通过getEnumConstants()获取该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。        Object[] enumConstants = declaringClass.getEnumConstants();        //枚举类个数,Weekday.values().length)        for (int i = 0; i < Weekday.values().length; i++) {            System.out.println("getEnumConstants,"+enumConstants[i]);          //SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY        }         //7、判断是否是枚举类型        System.out.println("declaringClass.isEnum(),"+declaringClass.isEnum());//true         //8、获取枚举变量的属性        System.out.println("编号,"+Week.MONDAY.getId()+",含义,"+Week.MONDAY.getMeaning());    }
复制代码

4、向上转型



这个东西意义何在,有待研究。


5、部分方法源码介绍


(1)valueof


(2)compareTo


五、枚举类中定义抽象方法


1、定义抽象方法



2、测试类


3、控制台输出


六、实现接口


1、接口


2、实现接口




3、测试类


//10、实现接口并调用接口中方法


MONDAY.StudyDataStrucure();


4、控制台输出


七、枚举实现单例


枚举单例(Enum Singleton)在 Effective Java 一书中提到,因为其功能完善,使用简洁,无偿地提供了序列化机制,在面对复杂的序列化或者反射攻击时依然可以绝对防止多次实例化等优点,被作者所推崇。


八、EnumMap


1、代码实例分析 EnumMap 和 HashMap


以 1 月到 6 月学习不同的 Java 项目为例。




2、控制台输出


3、结果分析


HashMap 对比 EnumMap


HashMap 和 EnumMap 的输出结果一致,证明枚举类型都可以使用,但是 EnumMap 作为枚举的专属的集合,我们没有理由再去使用 HashMap,毕竟 EnumMap 要求其 Key 必须为 Enum 类型。


由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值,毕竟数组是一段连续的内存空间,根据程序局部性原理,效率会相当高。


EnumMap 需要传递一个类型信息,即 Class 对象,通过这个参数 EnumMap 就可以根据类型信息初始化器内部数据结构,也可以初始化时传入一个 Map 集合。


key 值不能为 null。


4、简单的源码分析


(1)EnumMap 继承了 AbstractMap 类,因此 EnumMap 具备一般 map 的使用方法。


(2)由于 key 值基本固定,底层数组实现,效率更高


(3)获取 key 数组方法


(4)put()



(5)get()



(6)remove()


(7)containsKey()


以上就是 EnumMap 的源码分析,即内部有两个数组,长度相同,一个是以枚举为 key 的数组,一个是对应的值数组,key 不允许 null,value 可以为 null,键都有一个对应的索引,根据索引直接访问和操作其键数组和值数组,由于操作时都是数组,所以效率比 HashMap 高。


小结:


枚举除了不能继承,基本上可以将 enum 看做一个常规的类,Java 不允许使用为枚举常量赋值,enum 可以通过方法来显示赋值,可以添加普通方法、静态方法、抽象方法、构造方法。枚举可以实现接口,因为 enum 实际上都继承自 java.lang.Enum 类,而 Java 不支持多重继承,所以 enum 不能再继承其他类,当然也不能继承另一个 enum。


今日份分享已结束,请大家多多包涵和指点!

用户头像

还未添加个人签名 2021.04.20 加入

还未添加个人简介

评论

发布
暂无评论
清华教授,手把手带你深度解读Java枚举,理论与实战齐飞