Java

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

留言