对比Java 5.0前后的枚举类的定义和使用
一定义和特征
- 当某个类的对象有且只有有限个,并且是确定个数的。则可以把该类定义为枚举类;
- 当需要定义一组常量时,强烈建议使用枚举类;
- 如果枚举类中只有一个对象时,则该枚举类可以当做单例模式的一种实现方式。
Java 5开始之后,才开始引入枚举类,通过enum关键字来定义。
二定义和使用
1 JDK 5.0之前,自定义枚举类
JDK 5.0开始引入了自动装箱/拆箱,auto boxing/unboxing,StringBuilder类,注解等,都是Java 5开始有的。
注意:自定义枚举类的过程,就是考虑到枚举类的定义和特征的过程,即要有注释中的思路。
package js;
/**
* @Author:asher
* @Date:2021/7/11 23:05
* @Description:js
* @Version:1.0
*/
public class DBEnumTest {
public static void main(String[] args) {
System.out.println(DBEnum.MSSQL.getDbName());
DBEnum dbEnum = DBEnum.ORACLE;
System.out.println(dbEnum);
}
}
class DBEnum {
// 2定义若干常量,其实就是定义枚举类对象的成员变量(属性),要用private final来修饰
// 题外话,final变量初始化有3种常见方式:定义时;constructor、static code 实例化;
// 但是,我们这里不能用1,3种,为什么?这两种方式的实例化,将导致每个成员变量的属性值都是一致的
private final String dbName;
private final String dbVendor;
//1 私有化该类的构造方法,目的就是不让你在类外面实例化该类的对象。否则,如果可以在外面实例化的话,
// 则不能控制该类的对象的个数是有限个的;这和枚举类的特性相违背了,所有构造方法要私有化;
private DBEnum(String dbName,String dbVendor) {
this.dbName = dbName;
this.dbVendor = dbVendor;
}
// 3提供当前枚举类的多个对象(这里的多个,就是要确定的且是有限个的,这里创建了几个,就有几个)
//static关键字表示的是,将来可以直接通过DBEnum.ORACLE的方式来直接使用这个对象
//final关键字表示的是,这个对象一旦实例化,在当前类外面,就不可以再次对它执行初始化了
public static final DBEnum ORACLE = new DBEnum("Oracle", "@Oracle corporation");
public static final DBEnum DB2 = new DBEnum("DB2", "@IBM corp");
public static final DBEnum MSSQL = new DBEnum("SQL Server", "@Microsoft corp");
// 4提供枚举类对象属性(dbName,dbVendor)的getter,因为它们是private的,所以不能有setter
public String getDbName() {
return dbName;
}
public String getDbVendor() {
return dbVendor;
}
// 提供toString()方法
@Override
public String toString() {
return "DBEnum{" +
"dbName='" + dbName + '\'' +
", dbVendor='" + dbVendor + '\'' +
'}';
}
}
//执行结果:
SQL Server
DBEnum{dbName='Oracle', dbVendor='@Oracle corporation'}说明:自定义枚举类如果不覆写toString()方法的话,则其自动调用父类也就是Object类的toString()方法。
2 JDK 5中的enum定义枚举类
/**
* @Author:asher
* @Date:2021/7/12 08:38
* @Description:js
* @Version:1.0
*/
public class SeasonTest {
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
System.out.println(autumn.getSeasonName() + autumn.getSeasonDesc());
System.out.println(autumn);
}
}
enum Season{
SPRING("SPRING", "春天"),
SUMMER("SUMMER","夏天"),
AUTUMN("AUTUMN","秋天"),
WINTER("WINTER","冬天");
private final String seasonName;
private final String seasonDesc;
Season(String seasonName,String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
enum DBEnum1 {
// 3提供当前枚举类的多个对象(这里的多个,就是要确定的且是有限个的)
//由于是通过enum关键字来定义的枚举类,要求一上来就要定义枚举类的对象,本来应该是写成
//public static final DBEnum1 ORACLE = new DBEnum1("Oracle", "@Oracle corporation")
//这种格式的。但是,有多个对象,所以就把前面的public static final关键字省略了,接着把类名DBEnum1
//和后面的 = new DBEnum1也省略了。最后变成了"对象名(成员变量列表)"的格式了。
ORACLE("Oracle", "@Oracle corporation"),
DB2("DB2", "@IBM corp"),
MSSQL("SQL Server", "@Microsoft corp");
// 2定义若干常量,其实就是定义枚举类对象的成员变量,要用private final来修饰
// 题外话,final变量初始化有3种常见方式:定义时;constructor、static code 实例化;
// 但是,我们这里不能用1,3种,为什么?这两种方式的实例化,将导致每个成员变量的属性值都是一致的
private final String dbName;
private final String dbVendor;
//1 私有化该类的构造方法,目的就是不让你在类外面实例化该类的对象。否则,如果可以在外面实例化的话,
// 则不能控制该类的对象的个数是有限个的;这和枚举类的特性相违背了,所有构造方法要私有化;
DBEnum1(String dbName,String dbVendor) {
this.dbName = dbName;
this.dbVendor = dbVendor;
}
// 4提供枚举类对象属性(dbName,dbVendor)的getter,因为它们是private的,所以不能有setter
public String getDbName() {
return dbName;
}
public String getDbVendor() {
return dbVendor;
}
// 提供toString()方法
//通过enum定义的枚举类,不再需要重写toString()方法,因为,此时的枚举类是Enum类的子类,不再直接继承于Object类。
}注意,此时通过enum来定义枚举类的过程。①先直接把上面定义的DBEnum枚举类代码copy过来,②然后把关键字class改成enum;③把之前定义的第3步骤中的提供当前枚举类的多个对象的代码,向上提,放到类的定义的最开始代码处;④把公共部分的代码删除掉【public static final DBEnum】以及后面的【=new DBEnum】去掉。此时,这里的心路历程是有点儿类似于Java 8中定义接口时的思路,接口中的成员变量都是private static final修饰,方法都是public abstract修饰的。那么,我们的枚举类的对象也是这个思路。把【public static final DBEnum】以及后面的【=new DBEnum】省略掉了。然后直接跟上(),在括号()里写对象属性。⑤加上getter()方法;⑥通过enum定义的枚举类,其父类是Enum类,而不再是Object类,所以,我们不再需要@Override toString().且,枚举类不能再继承其它类,但是可以实现其它接口。
三枚举类常用的方法
1 values()
此方法其实是来源于Enum类的方法,返回值是一个当前枚举类类型的数组。
2 valueOf(“string”)
此方法其实是来源于Enum类的方法,根据传入的参数,获取枚举类中对象值是该字符串值的对象,返回值是一个当前枚举类的对象。
四Thread中的State枚举类举例
/**
* @Author:asher
* @Date:2021/7/11 17:22
* @Description:js
* @Version:1.0
*/
public class EnumTest {
public static void main(String[] args) {
Thread.State[] values = Thread.State.values();
for (Thread.State s: values ) {
System.out.println(s);
}
}
}可以看到Thread类中的线程状态,其实是通过一个内部的枚举类State来实现的。
五小结
最近,在项目组中有个功能模块用到枚举类,就赶紧找来枚举类的相关知识复习一下。
看了尚硅谷宋红康老师的Java基础知识中关于枚举类:https://www.bilibili.com/video/BV1Kb411W75N?p=498
p498–p501几个视频。
翻看了Thinking In Java关于枚举类的篇章,后面还要把第1008页左右开始的一章完全关于Enum类的内容,看两遍。
六补充
形如下述的枚举类定义是怎么演变来的?
enum CommonError{
INVALID_PARAMETER,USER_NOT_EXIST,UNKNOWN_ERROR
}
其实,是采用Java 5.0之后的方式来定义枚举类的;
1 一上来就定义枚举类的3个对象;这种定义方式是通过enum关键字来创建枚举类硬性规定的,必须一上来就定义;
2 由于该枚举类并没有定义成员变量,所以上面的每个枚举类对象后面就没有写成诸如“枚举类对象名 (成员变量) ” 了,就变成了INVALID_PARAMETER这种形式了;
3 既然没有定义成员变量,那么构造方法也就变成了无参构造方法了,而无参构造方法是每个类默认都有的。所以,这里也没有了显示声明无参构造方法了;
4 同理,没有定义成员变量,也就没有了获取成员变量的getter()方法了。
最终,采用enum关键字来定义枚举类就变成了这种光秃秃的简单形式了。
–2023.07.18


