Java枚举解读

Java枚举

枚举类概念的理解与定义

  • 一个类的对象是有限个,肯定的,咱们称此为枚举类。
  • 当须要定义和维护一组常量时,强烈建议使用枚举类。
  • 若是一个枚举类中只有一个对象,则能够做为单例模式的实现方式。
通俗的说:一个类被设计为包含固定实例数量的特殊类,咱们给他的定义是枚举类。

注意:
     1.枚举类不能被 new 出来,枚举类由于默认的类修饰符为 final 因此也不能被派生(继承),同理枚举类也不能为看成实现。
     2.枚举类自身能够实现接口,既能够进行统一实现重写接口抽象方法,也能够按照枚举类型单个实现重写。

枚举类的定义

关于枚举类的定义,这块主要想和你们分享两种方式java

  1. jdk 5.0以前,自定义枚举类方式
  2. jdk 5.0以后,Enum关键字方式定义

实践

1、准备工做

咱们新建一个 Java Project ,并建立一个包,以及一个测试类
spring

2、自定义枚举的三种方式(jdk 5.0 以前)

1. 定义一个抽象类,在抽象类中定义常量进行维护,咱们接下来以 Java 类库中的 Calendar 类示例来进行说明

新建一个类 EnumDemo01.java 代码以下:设计模式

package org.taoguoguo;
import java.util.Calendar;

/**
 * @author taoGG
 * @description jdk 5.0 以前 抽象类枚举方案Demo
 * @create 2020-09-13 14:20
 */
public class EnumDemo01 {
    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance();
        System.out.println(calendar.get(1));
    }
}

Console 结果输出:api

2020
Process finished with exit code 0

若是熟悉 Calendar API 的小伙伴 应该立刻能反应过来,这个是获取当前的年份,相似的值还有数组

3 - 一年中的第几个星期
4 - 一年中的第几个月
5 - 当前的日期 
......

可是这么多值,咱们怎么能记得住呢?万一我输入错误,随便取了一个范围怎么办?ide

没错,这是 jdk 5.0以前的痛点,为了解决实例数量固定,便于维护这些问题,在jdk 5.0以后更新Enum枚举类解决了这个问题。那在jdk 5.0以前官方是怎么作的呢?难道须要咱们一个个去记住 Calendar 的数字?工具

实际上官方自己,采用的就是咱们如今说的第一种方式,在抽象类中定义常量进行维护性能

如今咱们将代码作些修改:测试

package org.taoguoguo;
import java.util.Calendar;

/**
 * @author taoGG
 * @description jdk 5.0 以前 抽象类枚举方案Demo
 * @create 2020-09-13 14:20
 */
public class EnumDemo01 {
    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance();
        System.out.println(calendar.get(Calendar.YEAR));
    }
}

咱们运行进行输出:this

2020
Process finished with exit code 0

结果与以前一致,这时咱们就清楚,在开发过程当中做为开发者咱们确定愿意使用 Calendar.YEAR 这种写法,一来方便记忆,二来可读性高。那么官方的作法时怎样的呢?咱们点进去源码看一下

  1. 首先 Calendar 自己是一个抽象类,实现了序列化、克隆、以及比较排序接口,这边和咱们枚举没有太大关系,咱们继续往下看

  2. 在抽象类中,定义了不少个静态常量进行维护,而当咱们须要使用时,直接调用,这样就比咱们写一个个的具体值要方便和易用了。

2. 定义一个接口,在接口中定义常量维护枚举值

咱们新建一个interface CustomerInf.java

package org.taoguoguo;

/**
 * @author taoGG
 * @description 接口常量维护枚举值
 * @create 2020-09-13 15:47
 */
public interface CustomerInf {
   int RED = 1;
   int GREEN = 2;
   int BLUE = 3;
}

EnumTest 进行测试

package org.taoguoguo;

/**
 * @author taoGG
 * @description Java枚举测试类
 * @create 2020-09-13 14:54
 *
 */
public class EnumTest {
    public static void main(String[] args) {
        System.out.println(CustomerInf.RED);
    }
}

测试结果:

1
Process finished with exit code 0

这种作法咱们达到了和在抽象类中维护常量相同的目的。上面这两种作法都很是的简单易用,但也有弊端。好比咱们只知道一个状态值,当咱们要获取状态的属性或者相关的内容时,咱们该怎么作呢?

下面咱们使用第三种方式,自定义枚举类,这种基本上达到和 Enum 关键字相同的做用,但有一点不足就是会较为复杂

3.自定义枚举类,经过为类私有化构造器和固定实例对象进行枚举维护

新建一个class SeasonEnum.java,代码以下:

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 15:58
 */
public class SeasonEnum {
    //1.声明枚举对象的属性
    private final String seasonName;
    private final int code;

    //2.私有化类的构造器
    private SeasonEnum(String seasonName,int code){
        this.seasonName = seasonName;
        this.code = code;
    }

    //3.提供当前枚举类的多个对象 public static final
    public static final SeasonEnum SPRING = new SeasonEnum("春天",100);
    public static final SeasonEnum SUMMER = new SeasonEnum("夏天",200);
    public static final SeasonEnum AUTUMN = new SeasonEnum("秋天",300);
    public static final SeasonEnum WINTER = new SeasonEnum("冬天",400);

    //4.为类提供获取属性的方法
    public String getSeasonName() {
        return seasonName;
    }
    public int getCode() {
        return code;
    }
    //5.重写toString方法
    @Override
    public String toString() {
        return "SeasonEnum{" +
                "seasonName='" + seasonName + '\'' +
                ", code=" + code +
                '}';
    }
}

新建一个class SeasonEnumTest 进行测试,当咱们经过自定义枚举类引用实例对象时,以下图能够看到,咱们已经能够获取到咱们的枚举对象了。

获取到枚举对象,咱们固然也能够获取到对应的属性及方法,这种可用性就提升了不少,咱们在开发程序进行判断,能够根据各类枚举值的指定属性来进行,提升了代码的可维护性。

SeasonEnumTest 测试代码

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 16:04
 */
public class SeasonEnumTest {
    public static void main(String[] args) {
        SeasonEnum spring = SeasonEnum.SPRING;
        System.out.println("自定义枚举类对象:" + spring);
        System.out.println("自定义枚举类属性:" + spring.getSeasonName());
        System.out.println("自定义枚举类属性:" + spring.getCode());
    }
}

根据咱们上面的自定义枚举类方式,咱们基本已经实现了枚举的功能了,可是就像上面说到的,若是开发中枚举类型较多,开发多个这样的自定义枚举类会很是的耗时,因此 jdk 5.0 以后,推出了 Enum 关键字定义枚举类

3、Enum 关键字定义枚举类(jdk 5.0以后)

enum 全称为 enumeration,是jdk 5.0 中引入的新特性,在Java 中被 enum 关键字修饰的类型就是枚举类型

咱们经过代码来示例来说解和理解 enum 的用法,仍是用咱们刚刚自定以枚举类的例子,看看使用enum如何来写

新建一个Java class ,Kind 类型选择 enum 如图:

枚举类建立注意:

  • 枚举实例必须在 enum关键字声明的类中显式的指定(首行开始的以第一个分号结束)
  • 枚举不容许使用new,clone,反射,序列化手动建立枚举实例
package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 16:23
 */
public enum Season {
    SPRING("春天",100),
    SUMMER("夏天",200),
    AUTUMN("秋天",300),
    WINTER("冬天",400);

    private final String seasonName;
    private final int code;

    Season(String seasonName, int code){
        this.seasonName = seasonName;
        this.code = code;
    }

    public String getSeasonName() {
        return seasonName;
    }
    public int getCode() {
        return code;
    }
}

使用 SeasonTest 测试类进行测试:

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 16:27
 */
public class SeasonTest {
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);
    }
}

输出结果:

SPRING
Process finished with exit code 0

注意,在enmu 枚举类中若是没有重写 toString方法,会默认使用Enum类自己提供的 toString 方法,返回枚举类名称,由于定义的枚举类默认隐式继承于java.lang.Enum

1.枚举类主要方法介绍

  • values()  :该方法能够返回当前枚举类型的对象数组,能够很方便的遍历全部枚举值。通常咱们能够根据枚举类的相关属性经过此方法遍历获取对应的枚举对象及枚举值
  • valueOf(String str) : 根据枚举类名称获取枚举类对象
  • toString(): 默认使用 java.lang.Enum的 toString方法,返回当前对象常量的名称,枚举类推荐重写返回自定义友好描述
  • name(): 返回当前枚举对象名称,和toString做用上相似,当时toString支持重写,name方法是不能重写的,在本质上 toString 也是调用的 name方法,枚举定义 name 方法就是为了返回枚举对象名称,而 toString 应该根据须要进行重写
  • ordinal(): 返回当前枚举对象的序号, 实现了 Comparable 接口,代表它是支持排序的 能够经过 Collections.sort 进行自动排序比较此枚举与指定对象的顺序
  • compareTo(): 基于ordinal进行序号大小比较

方式演示代码,小伙伴们能够自行运行输出一下,看看各个方法的做用,熟悉一下相关的方法api

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 16:27
 */
public class SeasonTest {
    public static void main(String[] args) {
        System.out.println("========values()方法=======");
        for (Season season : Season.values()) {
            System.out.println(season);
        }
        System.out.println("===========================");
 
        System.out.println("========valueOf方法========");
        Season spring = Season.valueOf("SPRING");
        System.out.println(spring);
        System.out.println("===========================");

        System.out.println("========toString方法========");
        System.out.println(spring.toString());
        System.out.println("===========================");

        System.out.println("========name方法========");
        System.out.println(spring.name());
        System.out.println("===========================");

        System.out.println("========ordinal方法========");
        System.out.println(spring.ordinal());
        System.out.println("===========================");

        System.out.println("========compareTo方法========");
        System.out.println(spring.compareTo(Season.WINTER));
        System.out.println("===========================");
    }
}

2.枚举类对接口的实现方式

准备工做

新建一个EnumInf 接口,定义一个抽象方法

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 17:25
 */
public interface EnumInf {
    void show();
}
1.实现接口,在enum中统一实现抽象方法

新建一个EnumInf 接口,定义抽象方法 show()

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 17:25
 */
public interface EnumInf {
    void show();
}

新建一个OrderStatus 枚举类 实现 EnumInf 接口

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 17:27
 */
public enum OrderStatus implements EnumInf{

    SUCCESS(200,"交易成功"),
    Fail(500,"交易失败");

    private final int code;
    private final String desc;

    OrderStatus(int code, String desc){
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }
    public String getDesc() {
        return desc;
    }

    /**
     * 第一种方式,枚举统一重写接口抽象方法
     */
    @Override
    public void show() {
        System.out.println("订单枚举对象");
    }
}

进行测试

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 17:32
 */
public class OrderStatusTest {
    public static void main(String[] args) {
        OrderStatus success = OrderStatus.SUCCESS;
        success.show();
    }
}

输出结果

订单枚举对象

Process finished with exit code 0

跟咱们经常使用类实现没有什么区别,枚举也是能够统一实现的,那若是想针对不一样的枚举对象进行不一样状态的实现怎么办呢?好比咱们的OA系统、或者电商系统中,根据不一样状态 咱们须要回写对应的数据,下面咱们就来看看如何实现。

2.枚举对象分别实现接口中的抽象方法

案例跟接口统一实现一致,咱们这边修改一下OrderStatus 枚举类,代码以下

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 17:27
 */
public enum OrderStatus implements EnumInf{

    SUCCESS(200,"交易成功") {
        @Override
        public void show() {
            System.out.println("回写交易成功状态");
        }
    },
    Fail(500,"交易失败") {
        @Override
        public void show() {
            System.out.println("回写交易失败状态");
        }
    };

    private final int code;
    private final String desc;

    OrderStatus(int code, String desc){
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }
    public String getDesc() {
        return desc;
    }

}

咱们再修改下测试类代码:

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 17:32
 */
public class OrderStatusTest {
    public static void main(String[] args) {
        OrderStatus success = OrderStatus.SUCCESS;
        success.show();
        OrderStatus fail = OrderStatus.Fail;
        fail.show();
    }
}

输出结果

回写交易成功状态
回写交易失败状态

Process finished with exit code 0

经过这种方式就能够垂手可得地定义每一个枚举实例不一样的行为方式,也达到了咱们预期的效果,其实在开发过程当中根据枚举的设计和设计模式的铺垫能够极大的简化咱们的业务代码。

3.Enum枚举类的工具类及应用场景

1.EnumSet 和 EnumMap

Java 中提供了两个方便操做enum的工具类——EnumSet 和 EnumMap。

EnumSet 是枚举类型的高性能 Set 实现。它要求放入它的枚举常量必须属于同一枚举类型。

// EnumSet的使用
System.out.println("EnumSet展现");
EnumSet<OrderStatus> errSet = EnumSet.allOf(OrderStatus.class);
for (OrderStatus e : errSet) {
    System.out.println(e.name() + " : " + e.ordinal());
}

EnumMap 是专门为枚举类型量身定作的 Map 实现。虽然使用其它的 Map 实现(如HashMap)也能完成枚举类型实例到值得映射,可是使用 EnumMap 会更加高效:它只能接收同一枚举类型的实例做为键值,而且因为枚举类型实例的数量相对固定而且有限,因此 EnumMap 使用数组来存放与枚举类型对应的值。(计算机处理连续的资源使用局部内存效率更高)这使得 EnumMap 的效率很是高。

// EnumMap的使用
System.out.println("EnumMap展现");
EnumMap<StateMachine.Signal, String> errMap = new EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED, "红灯");
errMap.put(StateMachine.Signal.YELLOW, "黄灯");
errMap.put(StateMachine.Signal.GREEN, "绿灯");
for (Iterator<Map.Entry<StateMachine.Signal, String>> iter =errMap.entrySet().iterator(); iter.hasNext();) {
    Map.Entry<StateMachine.Signal, String> entry = iter.next();
    System.out.println(entry.getKey().name() + " : " + entry.getValue());
}
2.枚举类与 Switch 的配合使用

关于枚举与switch是个比较简单的话题,使用switch进行条件判断时,条件参数通常只能是整型,字符型。而枚举型确实也被switch所支持,在java 1.7后switch也对字符串进行了支持。

实践

新建一个 BizEnum 的java class,代码以下

package org.taoguoguo;

/**
 * @author taoGG
 * @description 企业类型枚举
 * @create 2020-09-13 21:24
 */
public enum BizEnum {

    COUNTRIES(101,"国有企业"),

    PRIVETE(102,"私营企业"),

    SOHO(103,"个体单位");

    private final int code;
    private final String desc;

    BizEnum(int code, String desc){
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }
    public String getDesc() {
        return desc;
    }

    //根据编码获取当前枚举对象的方法
    public static BizEnum getBizTypeByCode(int code){
        for (BizEnum bizEnum : BizEnum.values()) {
            if(code == bizEnum.getCode()){
                return bizEnum;
            }
        }
        return null;
    }
}

结合Switch进行测试

package org.taoguoguo;

/**
 * @author taoGG
 * @description
 * @create 2020-09-13 21:31
 */
public class BizTest {
    public static void main(String[] args) {
        BizEnum bizType = BizEnum.getBizTypeByCode(101);
        switch (bizType){
            case COUNTRIES:
                System.out.println("国有企业");
                break;
            case PRIVETE:
                System.out.println("私营企业");
                break;
            case SOHO:
                System.out.println("个体单位");
                break;
            default:
                System.out.println("创业中");
        }
    }
}

输出结果:

国有企业

Process finished with exit code 0

总结

  1. jdk 5.0以前咱们能够自定义枚举类,jdk 5.0以后使用enum关键字定义枚举类,枚举类默认继承自java.lang.Enum,使用枚举类将常量组织起来,便于统一管理。例如错误码状态机等场景中,较为合适使用枚举类。
  2. 枚举类经常使用方法介绍及枚举类实现抽象类、接口等抽象方法的两种方式。
  3. 枚举经常使用的工具类及与switch使用的场景。
相关文章
相关标签/搜索