文章目录


前言

      本篇文章是对枚举类的回炉学习,其中部分内容参考的是尚硅谷的课堂ppt教案,同时对其中知识点进行补充,这里帮尚硅谷打个广告报答一下:​​尚硅谷_Java零基础教程-java入门必备-适合初学者的全套完整版教程(宋红康主讲)​​。

      博客归类目录见:博客目录索引(持续更新)




一、认识枚举类

当类的对象只有有限个,确定的时,并且需要将其对象定义为一组常量时,建议使用​​枚举类​​,若枚举类只有一个对象则可以作为一种单例模式的实现方式。

  • 举例:JDK中的​​Tread.State​​​类中使用了枚举类,有​​NEW​​​、​​RUNNABLE​​​、​​BLOCKED​​​、​​WAITING​​​、​​TIMED_WAITING​​​、​​TERMINATED​​,它们分别用来表示线程的不同状态。

枚举类可以使用两种方式实现

  • ​JDK1.5​​之前需要自定义枚举类。
  • ​JDK 1.5 ​​​新增​​ enum​​关键字来用于定义枚举类。



二、自定义枚举类(jdk1.5之前方式)

枚举类的属性介绍

  1. 枚举类对象的属性不应该进行改动,应使用​​private final​​(常量)来修饰。
  2. 对于使用​​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​​定义的枚举类说明:

  1. 使用​​enum​​​定义的枚举类默认继承了​​java.lang.Enum​​类,因此不能继承其他类。
  2. 枚举类的构造器只能使用​​private​​权限修饰符。
  3. 枚举类实例在类中显示列出,使用​​,​​​分割​​;​​​结尾,可定义多个实例。列出的实例系统会自动添加​​public static final​​修饰。
  4. 必须在枚举类的第一行声明枚举类对象
  5. ​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​​,看一下该类:

Java学习笔记 15、枚举类_枚举类

  • 实现了​​Comparable​​接口以及序列化接口。

Java学习笔记 15、枚举类_枚举_02

  • ​ 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
}
}

Java学习笔记 15、枚举类_枚举类_03

Java学习笔记 15、枚举类_spring_04

  • ​IDEA​​中悬浮到这两个方法时的介绍。


我们自己进行反编译字节码查看一下下面这个枚举类


enum Code{
RED,BLUE,YELLOW;
}

首先使用​​javac 类.java​​​编译成字节码文件(.class后缀),之后使用​​javap Code​​进行反编译该字节码文件:

Java学习笔记 15、枚举类_java_05

之后再使用​​javap -v Code​​来查看Code.class字节码结构:

Java学习笔记 15、枚举类_spring_06

Java学习笔记 15、枚举类_枚举类_07

  • 这里看到是继承的​​Enum​​,查阅大佬博客推测出实际上是继承了​​ElementType​​,我们去看一下​​Enum​​类的定义
//这里泛型为E extends Enum<E> 表示该泛型必须是Enum的子类
public abstract class Enum<E extends Enum<E>>

Java学习笔记 15、枚举类_java_08

  • ​ElementType​​​是注解,并且是​​Enum​​类的子类。

Java学习笔记 15、枚举类_枚举_09

先暂且这么记着,对于我现在的水平不能窥探到更深的地步,日后再来深入挖掘。




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()​​方法,之后我们调用实例的方法就是指定其重写的方法。

总结

  1. 枚举类可以实现一个或多个接口,统一实现方法每个枚举实例都可以进行调用。
  2. 继承接口方式的使用方法有如上两种方式。



四、enum相关的类


Class类与enum相关


Java学习笔记 15、枚举类_java_10

  • ​isEnum()​​:能够判断该Class是否为枚举类。



与enum相关的工具类:EnumSet与EnumMap


​EnumSet​​:JDK1.5引入,该类设计充分考虑了速度因素,可以作为Enum的替代者,效率更高。

​EnumMap​​​:JDK1.5引入,是一个特殊的​​Map​​​,其key键是一个​​enum​​,可以进行快速检索。

Java学习笔记 15、枚举类_枚举_11

Java学习笔记 15、枚举类_枚举_12

暂且就先记录一下,深入使用内容后序若是使用到了进行补充。




参考文章

[1]. Java enum的用法详解

[2]. ​​Java中Enum类下的values()方法的由来​



我是长路,感谢你的阅读,如有问题请指出,我会听取建议并进行修正。
欢迎关注我的公众号:长路Java,其中会包含软件安装等其他一些资料,包含一些视频教程以及学习路径分享。
学习讨论qq群:891507813 我们可以一起探讨学习
注明:转载可,需要附带上文章链接