对比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