Java枚举类,你真的了解吗?

您所在的位置:网站首页 java枚举是什么意思 Java枚举类,你真的了解吗?

Java枚举类,你真的了解吗?

2022-12-30 05:35| 来源: 网络整理| 查看: 265

引言

文章相关代码已收录至我的github,欢迎star:lsylulu/myarticle枚举类可能在初学者的知识储备中比较陌生,但实际开发中,枚举类又不可或缺。枚举类能够统一管理一些全局的变量,封装对于他们的逻辑与方法。还能和switch-case结合,简化大量的if-else,让代码更加优雅。

文章导读

枚举类的作用枚举类基本性质枚举类基本方法含有抽象方法的枚举类枚举类的编译原理枚举类版单例模式结合反射获取枚举实例及属性总结一、为什么要用枚举类

关于这个问题,网上的答案很多,我大概整理一下。

1)出于类型安全考虑,没用枚举类之前,常用静态常量来表示。比如对于性别的表示,public static final int MAN = 0; public static final int WOMAN = 1; 这样的性别定义实际上是一个整型数据,其一,这些变量完全可用来做加减运算,当然我们原意并非如此;其二,意义不明,当我们debug的时候,本来向输出‘男’,结果输出0。于是我们不得不去前面寻找0表示的意义,特别是看别人的代码时,会很懵逼。

2)代码更优雅一个大一些的程序里面,可能要用到成百上千的静态常量,如果全写在一个文件里面,容易造成命名混淆,程序读起来也比较麻烦。

3)枚举类能方便我们定义自己想要的类型枚举便于记忆和使用,并且相当于一个接口。使用时只需要封装内部的数据类型,并且限定数据域。而且对于不同的枚举变量,可以调用不同的处理方法(实现枚举类的抽象方法可以做到这一点)。

二、枚举类的定义及其内部方法讲解

如果不能充分的理解枚举类的作用,不要担心,看完后就明白啦~

2.1 枚举类基础public enum Week { //本文的枚举类变量,枚举类实例,name属性指的就是MONDAY //这类的变量 MONDAY(0,"星期一"), TUESDAY(1,"星期二"), WEDNESDAY(2,"星期三"), THURSDAY(3,"星期四"), FRIDAY(4,"星期五"), SATURDAY(5,"星期六"), //最后一个类型必须要用分号结束 SUNDAY(6,"星期日"); private int num; private String desc; /** * 构造方法必然是private修饰的 * 就算不写,也是默认的 * * @param num * @param desc */ private Week(int num, String desc) { this.num=num; this.desc = desc; } public String getDesc() { return desc; } public int getNum() { return num; }

定义了一个简单了关于星期的枚举类,如果是第一次见,也不要着急,看多了就会写了。看完代码,就从关于枚举类的基础开始讲解。1)使用enum定义的枚举类默认继承了java.lang.Enum,实现了java.lang.Comparable接口,且不能继承其他类,也不可以被继承。但枚举类可以实现一个或多个接口。想深入学习必须挖Enum的源码。2)枚举类的所有实例必须放在第一行显示,不需使用new,不需显示调用构造方法,每个变量都是public static final修饰的,最终以分号结束。在之后的反编译中,我们就可以理解枚举类其实也是颗语法糖。3)枚举类的构造方法是私有的,默认的就是private,定义的时候不加也没事。4)switch()参数可以使用enum。这个在后文有详细代码。5)非抽象枚举类默认是final的但定义的时候加上final却编译不通过。我们通过后续的反编译可以得到验证。6)枚举类可以有抽象方法,但是必须在它的实例中实现。后续也会验证这一条。

2.2 name成员变量和常用方法

记下我在这挖的几个坑,先来看看枚举类的name成员变量和常用方法。

2.2.1 name成员变量和常用方法

name成员变量

Enum中的重要成员变量

2.2.2 枚举类的常用方法

valueOf()

返回当前枚举类的name属性,如果没有,则throw new java.lang.IllegalArgumentException()。具体可以查看java.lang.Enum源码。

values()

编译器自动生成的方法,Enum中并没有该方法,返回包括所有枚举变量的数组。

toString(),name()

很简单,两个方法一样,返回当前枚举类变量的name属性。如果默认的toString()不能满足需求,我们可以结合switch来灵活的实现toString()方法

/** * 用switch重写toString方法,提高代码健壮性 * @return */ @Override public String toString() { //switch支持Enum类型 switch (this) { case MONDAY: return "今天星期一"; case TUESDAY: return "今天星期二"; case WEDNESDAY: return "今天星期三"; case THURSDAY: return "今天星期四"; case FRIDAY: return "今天星期五"; case SATURDAY: return "今天星期六"; case SUNDAY: return "今天星期日"; default: return "Unknow Day"; } }ordinal()

枚举类会给所有的枚举变量一个默认的次序,该次序从0开始,是根据我们定义的次序来排序的。而ordinal()方法就是获取这个次序(或者说下标)。

compareTo()

比较的是两个枚举变量的次序,返回两个次序相减后的结果,具体可以扒源码。

看看如下示例:

public static void main(String[] args) { //通过values()获取枚举数组 Week[] weeks = Week.values(); //遍历Week枚举类 for (Week day : weeks) { System.out.println("name:" + day.name() + ",desc:" + day.getDesc()); } //不符合则抛出java.lang.IllegalArgumentException System.out.println(Week.valueOf("MONDAY")); //返回对应的name属性 System.out.println(Week.FRIDAY.toString()); //返回4,根据我们定义的次序,从0开始。如果在定义时调换FRIDAY //的次序,返回的数字也会对应的变化 System.out.println(Week.FRIDAY.ordinal()); }

输出:

name:MONDAY,desc:星期一 name:TUESDAY,desc:星期二 name:WEDNESDAY,desc:星期三 name:THURSDAY,desc:星期四 name:FRIDAY,desc:星期五 name:SATURDAY,desc:星期六 name:SUNDAY,desc:星期日 今天星期一 今天星期五 4

2.3 含有抽象方法的枚举类

如果写抽象方法,枚举类的所有实例必须实现抽象方法。MONDAY本身就是一个AbstractWeek对象的引用。在初始化这个枚举类的时候,等同于执行的是AbstractWeek MONDAY= new AbstractWeek(0,"星期一")。然后用匿名内部类的方式实现getNextDay()。

/** * 枚举类可以有抽象方法,但是必须在它的实例中实现 */ public enum AbstractWeek { MONDAY(0,"星期一") { @Override public AbstractWeek getNextDay() { return TUESDAY; } }, TUESDAY(1,"星期二") { @Override public AbstractWeek getNextDay() { return WEDNESDAY; } }, WEDNESDAY(2,"星期三") { @Override public AbstractWeek getNextDay() { return THURSDAY; } }, THURSDAY(3,"星期四") { @Override public AbstractWeek getNextDay() { return FRIDAY; } }, FRIDAY(4,"星期五") { @Override public AbstractWeek getNextDay() { return SATURDAY; } }, SATURDAY(5,"星期六") { @Override public AbstractWeek getNextDay() { return SUNDAY; } }, SUNDAY(6,"星期日") { @Override public AbstractWeek getNextDay() { return MONDAY; } }; private int num; private String desc; AbstractWeek(int num,String desc) { this.num = num; this.desc=desc; } //一个抽象方法 public abstract AbstractWeek getNextDay(); public static void main(String[] args) { String nextDay=AbstractWeek.MONDAY.getNextDay().toString(); System.out.println(nextDay); } }

编译后所有实例都会成为内部类,相当于每个实例用匿名内部类的形式实现getNextDay的方法。如:

AbstractWeek MONDAY= new AbstractWeek (){ @Override public AbstractWeek getNextDay() { return TUESDAY; } };三、为什么说枚举类是语法糖

这个问题需要从原理角度阐述,我们通过反编译来查看AbstractWeek,可以发现继承了Enum,里面的所有成员变量都是public static final修饰的!而且values()方法是编译器生成的。其实每一个实例都是一个内部类,在项目路径下会生成AbstractWeek$1.class-AbstractWeek$7.class。每个内部类又都实现了getNextDay()方法。因此,定义枚举实例的时候代码如此的优雅,完全是语法糖给我们的甜头呀!

public abstract class AbstractWeek extends java.lang.Enum { public static final AbstractWeek MONDAY; public static final AbstractWeek TUESDAY; public static final AbstractWeek WEDNESDAY; public static final AbstractWeek THURSDAY; public static final AbstractWeek FRIDAY; public static final AbstractWeek SATURDAY; public static final AbstractWeek SUNDAY; public static solution1.AbstractWeek[] values(); Code: 0: getstatic #2 // Field $VALUES:[Lsolution1/AbstractWeek; 3: invokevirtual #3 // Method "[Lsolution1/AbstractWeek;".clone:()Ljava/lang/Object; 6: checkcast #4 // class "[Lsolution1/AbstractWeek;" 9: areturn ...... }

四、枚举版单例与反射获取枚举对象

4.1 学会枚举创建单例模式

我们可以充分利用枚举默认构造方法私有化的性质来实现单例。由于里面的成员变量都是final修饰的,因此不会有线程不安全的问题。枚举类版的单例模式,就是这么简单。

public enum EnumSingleton{ INSTANCE; }

4.2 反射获取枚举对象

// 1.得到枚举类对象 Class clazz = AbstractWeek.class; // 2.得到枚举类中的所有实例 Object[] enumInstances = clazz.getEnumConstants(); Method getDesc= clazz.getMethod("getDesc"); Method getNextDay= clazz.getMethod("getNextDay"); for (Object instance : enumInstances){ // 3.调用对应方法,得到枚举类中实例的值与实现的抽象方法 System.out.println("desc=" + getDesc.invoke(obj) + "; nextDay=" + getNextDay.invoke(obj)); }总结

本文主要讲述了枚举的作用,语法,基本方法,实现抽象方法,实现原理,单例与反射。我在2.1中挖的坑也都填上了。真正吸收的话还得多看多练。掌握了这些,在实际编码的时候也会舒坦些。网上的博客对于枚举的知识都比较零碎,自我感觉搬运的干货还是比较全面的。

如果喜欢我的文章欢迎关注我的专栏~

参考文章:Enum详解



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3