一、认识枚举类
当类的对象只有有限个,确定的时,并且需要将其对象定义为一组常量时,建议使用枚举类,若枚举类只有一个对象则可以作为一种单例模式的实现方式。
举例:JDK中的Tread.State类中使用了枚举类,有NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,它们分别用来表示线程的不同状态。
枚举类可以使用两种方式实现:
JDK1.5之前需要自定义枚举类。
JDK 1.5 新增 enum关键字来用于定义枚举类。
二、自定义枚举类(jdk1.5之前方式)
枚举类的属性介绍:
枚举类对象的属性不应该进行改动,应使用private final(常量)来修饰。
对于使用private final的常量对象应该在有参构造器中进行赋值。
自定义枚举类如下:
class Season{ //1、将注解类中的属性设置为private final常量,不能进行改动 private final String SeasonName; private final String SeasonDesc; //2、私有构造器,防止外部构造对象 private Season(String seasonName, String seasonDesc) { SeasonName = seasonName; SeasonDesc = seasonDesc; } public String getSeasonName() { return SeasonName; } public String getSeasonDesc() { return SeasonDesc; } @Override public String toString() { return "Season{" + "SeasonName='" + SeasonName + '\'' + ", SeasonDesc='" + SeasonDesc + '\'' + '}'; } //3、提供当前枚举类的多个对象,其修饰符为:public static final,可以被外界获取 public static final Season SPRING = new Season("春天","百花齐放"); public static final Season SUMMER = new Season("夏天","夏日炎炎"); public static final Season AUTUMN = new Season("秋天","秋高气爽"); public static final Season WINTER = new Season("冬天","白雪皑皑"); } /** * @ClassName EnumExer * @Author ChangLu * @Date 2021/2/21 15:50 * @Description TODO */ public class EnumExer { public static void main(String[] args) { Season spring = Season.SPRING; Season summer = Season.SUMMER; Season autumn = Season.AUTUMN; Season winter = Season.WINTER; System.out.println(spring);//Season{SeasonName='春天', SeasonDesc='百花齐放'} System.out.println(summer);//Season{SeasonName='夏天', SeasonDesc='夏日炎炎'} System.out.println(autumn);//Season{SeasonName='秋天', SeasonDesc='秋高气爽'} System.out.println(winter);//Season{SeasonName='冬天', SeasonDesc='白雪皑皑'} } }
注意其中三个关键点
第一个就是属性应该设置为private final是不允许外界使用set方法进行赋值的。
第二个就是构造器为private,外界不能通过构造器来构造枚举类对象。
第三个就是提供枚举类对象,权限修饰符为public static final 应该是能够被外界直接获取使用的。
这是自定义枚举类的方式,一般我们使用jdk1.5之后的这种方式来创建枚举类。
三、enum定义枚举类(jdk1.5新增)
3.1、使用enum定义枚举类
使用enum定义的枚举类说明:
使用enum定义的枚举类默认继承了java.lang.Enum类,因此不能继承其他类。
枚举类的构造器只能使用private权限修饰符。
枚举类实例在类中显示列出,使用,分割;结尾,可定义多个实例。列出的实例系统会自动添加public static final修饰。
必须在枚举类的第一行声明枚举类对象。
JDK 1.5 中可以在 switch 表达式中使用Enum定义的枚举类的对象作为表达式, case 子句可以直接使用枚举值的名字, 无需添加枚举类作为限定。
enum定义枚举类如下:
enum Season{ //,分割 ;结尾 这里实际上定义了4个public static final的实例(会自动添加修饰) //SPRING("春天","百花齐放") =>(相当于) public static final Season SPRING = new Season("春天","百花齐放"); SPRING("春天","百花齐放"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","白雪皑皑"); private final String SeasonName; private final String SeasonDesc; private Season(String seasonName, String seasonDesc) { SeasonName = seasonName; SeasonDesc = seasonDesc; } public String getSeasonName() { return SeasonName; } public String getSeasonDesc() { return SeasonDesc; } } public class EnumExer { public static void main(String[] args) { Season spring = Season.SPRING; Season summer = Season.SUMMER; Season autumn = Season.AUTUMN; Season winter = Season.WINTER; System.out.println(spring);//SPRING System.out.println(summer);//SUMMER System.out.println(autumn);//AUTUMN System.out.println(winter);//WINTER } }
使用enum定义时,对于实例需要直接写在类的最前面,若是有多个实例可以使用,分割开。
这里输出时使用的是Enum类中的toString()输出的是当前枚举常量的名称。
3.2、认识java.lang.enum类
之前说到使用enum定义枚举类的会继承java.lang.enum,看一下该类:
实现了Comparable接口以及序列化接口。
static static <T extends Enum<T>> T valueOf():传递枚举类型的Class对象和枚举常量名称,会得到与参数匹配的枚举常量。
String toString():默认会得到当前枚举常量的名称。可以通过重写这个方法来使得到的结果更易读。
final boolean equals():该方法是常量(final)方法无法重写,在Enum中是使用==来比较两个枚举常量是否相同。其存在是为了在Set、List和Map中使用。
final int hashCode():与equals()同样是常量方法无法被子类重写。
final Class<E> getDeclaringClass():得到枚举常量所属枚举类型的Class对象。可以用它来判断两个枚举常量是否属于同一个枚举类型。
final String name():也是得到当前枚举常量的名称,与toString()都是返回name属性。
final int ordinal():得到当前枚举常量定义时的序列号(从0开始)。
final int compareTo():这是实现Comparable接口的方法,来比较两个枚举常量的大小(根据定义实例的声明顺序排列)。
final Object clone():因枚举类型不能被克隆,防止子类实现克隆方法,该方法定义为常量方法,并且直接抛出CloneNotSupportedException异常。
常用方法介绍:前两个是编译器插入到enum中的方法并不是默认继承Enum类获取的方法
values():该方法是编译器插入到enum定义中的static方法,直接调用枚举类的方法,返回一个你定义的枚举类数组,方便进行遍历该枚举类的常量。
valueOf(String str):通过传入一个字符串来获取该枚举类的一个枚举实例,必须是枚举类对象的实例名称,否则IllegalArgumentException异常。
toString():这是继承Enum类的方法,返回实例名称,尽量要重写它。
提出疑问这两个方法是怎么来的?
查阅博客,通过反编译字节码文件认为是实现了java.lang.annotation.ElementType中的values()与valueOf方法,这个过程是在编译器中自动生成的。
测试编译器中生成的两个方法:
enum Code{ RED,BLUE,YELLOW; } public class Test { public static void main(String[] args) { //测试Code枚举类的values()方法 Code[] values = Code.values(); for (Code value : values) { System.out.println(value); } //RED //BLUE //RED //测试valueOf()方法 System.out.println(Code.valueOf("RED"));//RED } }
IDEA中悬浮到这两个方法时的介绍。
我们自己进行反编译字节码查看一下下面这个枚举类
enum Code{ RED,BLUE,YELLOW; }
首先使用javac 类.java编译成字节码文件(.class后缀),之后使用javap Code进行反编译该字节码文件:
之后再使用javap -v Code来查看Code.class字节码结构:
这里看到是继承的Enum,查阅大佬博客推测出实际上是继承了ElementType,我们去看一下Enum类的定义
//这里泛型为E extends Enum<E> 表示该泛型必须是Enum的子类 public abstract class Enum<E extends Enum<E>>
ElementType是注解,并且是Enum类的子类。
参考博客: Java中Enum类下的values()方法的由来
先暂且这么记着,对于我现在的水平不能窥探到更深的地步,日后再来深入挖掘。
3.3、枚举类实现接口
现有一个接口如下:
interface Info{ void show(); }
直接在enum枚举类中实现抽象方法
//实现该接口 enum Season implements Info{ SPRING("春天","百花齐放"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","白雪皑皑"); private String SeasonName; private String SeasonDesc; private Season(String seasonName, String seasonDesc) { SeasonName = seasonName; SeasonDesc = seasonDesc; } //直接实现抽象方法show() @Override public void show() { System.out.println("这里是描述四个季节的枚举类"); } } public class EnumExer { public static void main(String[] args) { Season.SPRING.show();//这里是描述四个季节的枚举类 Season.SUMMER.show();//这里是描述四个季节的枚举类 } }
可以看到多个实例调用的都是同一个show()方法。
枚举类中实例重写show()方法
enum Season implements Info{ //这里对该实例进行了方法重写(应该是重写了Season该类的show()方法) SPRING("春天","百花齐放"){ @Override public void show() { System.out.println("SPRING实例的show()方法调用了...."); } }, SUMMER("夏天","夏日炎炎"){ @Override public void show() { System.out.println("SUMMER实例的show()方法调用了...."); } }, AUTUMN("秋天","秋高气爽"), WINTER("冬天","白雪皑皑"); private String SeasonName; private String SeasonDesc; private Season(String seasonName, String seasonDesc) { SeasonName = seasonName; SeasonDesc = seasonDesc; } @Override public void show() { System.out.println("这里是描述四个季节的枚举类"); } } public class EnumExer { public static void main(String[] args) { Season.SPRING.show();//SPRING实例的show()方法调用了.... Season.SUMMER.show();//SUMMER实例的show()方法调用了.... Season.AUTUMN.show();//这里是描述四个季节的枚举类 } }
可以看到这里是在对应实例后添加{},其中重写了show()方法,之后我们调用实例的方法就是指定其重写的方法。
总结:
枚举类可以实现一个或多个接口,统一实现方法每个枚举实例都可以进行调用。
继承接口方式的使用方法有如上两种方式。
四、enum相关的类
Class类与enum相关
isEnum():能够判断该Class是否为枚举类。
与enum相关的工具类:EnumSet与EnumMap
EnumSet:JDK1.5引入,该类设计充分考虑了速度因素,可以作为Enum的替代者,效率更高。
EnumMap:JDK1.5引入,是一个特殊的Map,其key键是一个enum,可以进行快速检索。
暂且就先记录一下,深入使用内容后序若是使用到了进行补充。