使用枚举实现“状态”转换限制

简介: 我是小假 期待与你的下一次相遇 ~

枚举类

JDK5 提供了一种新的特殊的类——枚举类,一般在类对象有限且固定的场景下使用,用来替代类中定义常量的方式。枚举相较于常量更加直观且类型安全。

枚举类的使用非常简单,用 enum 关键字来定义,多个枚举变量直接用逗号隔开。先来定义一个简单的枚举类 OrderStatus.java

  1. public enum OrderStatus {
  2.    //未支付、已支付、退款中、退款成功、退款失败;
  3.    NO_PAY, PAY, REFUNDING, REFUNDED, FAIL_REFUNDED, ;
  4. }

在其他类中使用 enum 变量的时候,只需要【类名.变量名】就可以了,和使用静态变量一样。另外,枚举类型可以确保 JVM 中仅存在一个常量实例,所以可以放心的使用“ ==”来比较两个变量。

注意事项:

  1. 枚举类的第一行必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其它的东西,这个分号就不能省略。建议不要省略!
  2. 枚举变量最好大写,多个单词之间使用“_”隔开(比如:NO_PAY)。
    反编译
    可以先通过 javac 命令或者 IDEA 的编译功能将OrderStatus.java 编译为OrderStatus.class 字节码文件,然后用DJ Java Decompiler 反编译器对 .class 文件进行反编译。
  1. public final class OrderStatus extends Enum
  2. {
  3. //该方法会返回包括所有枚举变量的数组,可以方便的用来做循环。
  4. public static OrderStatus[] values()
  5. {
  6.     return (OrderStatus[])$VALUES.clone();
  7. }
  8. //根据传入的字符串,转变为对应的枚举变量。
  9. //前提是传的字符串和定义枚举变量的字符串一抹一样,区分大小写。
  10. //如果传了一个不存在的字符串,那么会抛出异常。
  11. public static OrderStatus valueOf(String name)
  12. {
  13.     return (OrderStatus)Enum.valueOf(com/itcast/java/enumpack/OrderStatus, name);
  14. }
  15. private OrderStatus(String s, int i)
  16. {
  17.     super(s, i);
  18. }
  19. public static final OrderStatus NO_PAY;
  20. public static final OrderStatus PAY;
  21. public static final OrderStatus REFUNDING;
  22. public static final OrderStatus REFUNDED;
  23. public static final OrderStatus FAIL_REFUNDED;
  24. private static final OrderStatus $VALUES[];
  25. static
  26. {
  27.     NO_PAY = new OrderStatus("NO_PAY", 0);
  28.     PAY = new OrderStatus("PAY", 1);
  29.     REFUNDING = new OrderStatus("REFUNDING", 2);
  30.     REFUNDED = new OrderStatus("REFUNDED", 3);
  31.     FAIL_REFUNDED = new OrderStatus("FAIL_REFUNDED", 4);
  32.     $VALUES = (new OrderStatus[] {
  33.         NO_PAY, PAY, REFUNDING, REFUNDED, FAIL_REFUNDED
  34.     });
  35. }
  36. }
  1. 如源码所示:
  • 编译器会自动创建一个 final 类型的类继承 Enum 类,所以枚举类不能被继承。
  • 会自动生成私有构造方法,当然也可以定义构造方法,但必须是私有的,这样就不能在别处声明此类的对象了。
  • 枚举项会被自动添加 public static final 修饰,并定义为 OrderStatus 类型,并在静态代码块中被初始化。
  • 并提供了 values()valueOf(String name) 的静态方法。

定义的枚举变量实际上是编译器自动生成了构造函数。

所有枚举类都是 Enum 的子类,枚举类可以实现一个或多个接口。

Enum

Enum 是所有 Java 语言枚举类型的公共基类,实现了 Comparable 和 Serializable 接口。它包含 final 类型的 name 和 ordinal (此枚举常量的序号,从0开始)属性,下面来了解下它的方法

  • protected Enum(String name, int ordinal);——构造方法;
  • public String toString();——返回 name 字段,即枚举定义枚举变量的字符串;
  • protected final Object clone();——抛出 CloneNotSupportedException 异常,保证枚举类永远不会被克隆;
  • public final ClassgetDeclaringClass();——返回与此枚举常量的枚举类型对应的类对象;
  • protected final void finalize();—— 枚举类不能有 finalize 方法;
  • readObject(ObjectInputStream in);&readObjectNoData();—— 抛出InvalidObjectException 异常,防止默认反序列化; 扩展
  1. 枚举类中可以自定义属性自定义的属性值最好用 private final 修饰,防止生成的 set 方法在使用时修改属性值,使代码更加安全。
  2. 枚举类中可以自定义构造函数构造函数必须为 private 修饰,防止在别处声明此类对象。
  3. 枚举类可以自定义方法,枚举项可以选择性覆盖自定义的方法。 ```java public enum OrderStatus{ NO_PAY(“未支付”,0), PAY(“已支付”,1){
  1. @Override
  2. public void printOrderStatus() {
  3.     System.out.println("已支付");
  4. }
  1. }, REFUNDING(“退款中”,2), REFUNDED(“退款成功”,3), FAIL_REFUNDED(“退款失败”,4), ;
    private final String name; private final int status;
    private OrderStatus(String name,int status){
  1. this.name = name;
  2. this.status = status;
  1. }
    public void printOrderStatus(){
  1. System.out.println("打印订单状态");
  1. } }

public class EnumTest { public static void main(String[] args) { OrderStatus.PAY.printOrderStatus(); OrderStatus.NO_PAY.printOrderStatus(); } }

  1. ![2021-09-15-14-20-13-906674.png](https://cdn.nlark.com/yuque/0/2021/png/396745/1631686837519-0da4ad7a-7f02-426b-b527-74a019ce512c.png#clientId=u5f62019f-13c2-4&from=ui&id=u0c5fd862&margin=%5Bobject%20Object%5D&name=2021-09-15-14-20-13-906674.png&originHeight=111&originWidth=619&originalType=binary&ratio=1&size=8193&status=done&style=none&taskId=u71dcecc7-4898-4b44-b2fd-aa77e60205c)<br />枚举类也可以有抽象方法,但是枚举项必须重写该方法。
  2. 1. 枚举类实现接口与普通类一样,实现接口的时候需要实现接口的抽象方法,也可以让枚举类的不同对象实现不同的行为。
  3. ```java
  4. //定义一个接口
  5. public interface Order {
  6.    void printOrderStatus();
  7. }
  8. //枚举类实现该接口
  9. public enum OrderStatus implements Order{
  10.    NO_PAY("未支付",0){
  11.        @Override
  12.        public void printOrderStatus() {
  13.            System.out.println("未支付");
  14.        }
  15.    },
  16.    PAY("已支付",1){
  17.        @Override
  18.        public void printOrderStatus() {
  19.            System.out.println("已支付");
  20.        }
  21.    },
  22.    REFUNDING("退款中",2){
  23.        @Override
  24.        public void printOrderStatus() {
  25.            System.out.println("退款中");
  26.        }
  27.    },
  28.    REFUNDED("退款成功",3){
  29.        @Override
  30.        public void printOrderStatus() {
  31.            System.out.println("退款成功");
  32.        }
  33.    },
  34.    FAIL_REFUNDED("退款失败",4){
  35.        @Override
  36.        public void printOrderStatus() {
  37.            System.out.println("退款失败");
  38.        }
  39.    },
  40.    ;
  41.    private final String name;
  42.    private final int status;
  43.    private OrderStatus(String name,int status){
  44.        this.name = name;
  45.        this.status = status;
  46.    }
  47. }

此时查看编译后的文件,会发现除了生成 OrderStatus.class 文件之外,还生成了多个 .class 文件:

它们是 OrderStatus.class 中生成的匿名内部类的文件。

状态转换

需求

订单是电商项目中不可缺少的组成部分,而订单状态的转换也是经常讨论的问题。都知道订单状态的转换是有一定的逻辑性的,不可以随意转换。

例:购买某个商品,只是把它加入了购物车,此时应该是未支付状态。如果来个请求想把它转换为退款状态,那么系统应该抛出提示信息“状态转换失败,请先完成购买!”

接下来就用枚举来完成一下订单状态转换的限制。

实现

枚举类定义:

  1. public enum OrderStatus{
  2.    NO_PAY("未支付",0){
  3.        @Override
  4.        public Boolean canChange(OrderStatus orderStatus) {
  5.            switch (orderStatus){
  6.                case PAY:
  7.                    return true;
  8.                default:
  9.                    return false;
  10.            }
  11.        }
  12.    },
  13.    PAY("已支付",1){
  14.        @Override
  15.        public Boolean canChange(OrderStatus orderStatus) {
  16.            //因为退款接口一般都会有延迟,所以会先转化为“退款中”状态
  17.            switch (orderStatus){
  18.                case REFUNDING:
  19.                    return true;
  20.                default:
  21.                    return false;
  22.            }
  23.        }
  24.    },
  25.    REFUNDING("退款中",2){
  26.        @Override
  27.        public Boolean canChange(OrderStatus orderStatus) {
  28.            switch (orderStatus){
  29.                case REFUNDED:
  30.                case FAIL_REFUNDED:
  31.                    return true;
  32.                default:
  33.                    return false;
  34.            }
  35.        }
  36.    },
  37.    REFUNDED("退款成功",3),
  38.    FAIL_REFUNDED("退款失败",4),
  39.    ;
  40.    private final String name;
  41.    private final int status;
  42.    private OrderStatus(String name,int status){
  43.        this.name = name;
  44.        this.status = status;
  45.    }
  46.    //自定义转换方法
  47.    public Boolean canChange(OrderStatus orderStatus){
  48.        return false;
  49.    }
  50. }

调用方法:

  1. public class EnumTest {
  2.    public static void main(String[] args) {
  3.        Boolean aBoolean = OrderStatus.NO_PAY.canChange(OrderStatus.PAY);
  4.        String statusStr = aBoolean?"可以":"不可以";
  5.        System.out.println("是否可以完成状态转换:"+ statusStr);
  6.        Boolean flag = OrderStatus.REFUNDED.canChange(OrderStatus.FAIL_REFUNDED);
  7.        String flagStr = flag?"可以":"不可以";
  8.        System.out.println("是否可以完成状态转换:"+ flagStr);
  9.    }
  10. }

返回结果:

这样就用枚举类实现了订单状态转换的限制。此例子只是为状态转换提供一种思路,具体的流程还需要根据自己系统中的业务来具体处理。


相关文章
|
11月前
|
存储 Java 编译器
说一说关于序列化/反序列化中的细节问题
我是小假 期待与你的下一次相遇 ~
209 1
|
监控 Java Unix
6个Java 工具,轻松分析定位 JVM 问题 !
本文介绍了如何使用 JDK 自带工具查看和分析 JVM 的运行情况。通过编写一段测试代码(启动 10 个死循环线程,分配大量内存),结合常用工具如 `jps`、`jinfo`、`jstat`、`jstack`、`jvisualvm` 和 `jcmd` 等,详细展示了 JVM 参数配置、内存使用、线程状态及 GC 情况的监控方法。同时指出了一些常见问题,例如参数设置错误导致的内存异常,并通过实例说明了如何排查和解决。最后附上了官方文档链接,方便进一步学习。
2952 4
|
存储 人工智能 搜索推荐
详解MySQL字符集和Collation
MySQL支持了很多Charset与Collation,并且允许用户在连接、Server、库、表、列、字面量多个层次上进行精细化配置,这有时会让用户眼花缭乱。本文对相关概念、语法、系统变量、影响范围都进行了详细介绍,并且列举了有可能让字符串发生字符集转换的情况,以及来自不同字符集的字符串进行比较等操作时遵循的规则。对于最常用的基于Unicode的字符集,本文介绍了Unicode标准与MySQL中各个字符集的关系,尤其详细介绍了当前版本(8.0.34)默认字符集utf8mb4。
3029 82
|
SQL 关系型数据库 MySQL
做MySQL 的并发控制
本文以 MySQL 8.0.35 的代码为例,尝试对 MySQL 中的并发访问控制进行一个整体的介绍。
|
Java API Spring
Java小抄 使用StopWatch输出执行耗时
通过本文的介绍,我们详细讲解了如何使用 `StopWatch` 类测量代码执行时间。`StopWatch` 提供了简单而强大的功能,帮助我们精确分析代码的性能瓶颈,优化程序效率。希望本文能帮助您更好地理解和应用 `StopWatch`,在实际开发中提高代码性能和质量。
2467 80
|
10月前
|
JSON 安全 Java
Java 中的枚举
我是小假 期待下一次相遇 ~
115 3
|
10月前
|
机器学习/深度学习 人工智能 自然语言处理
从五子棋到DeepSeek:揭开模式匹配的奥秘
本文通过五子棋AI与大语言模型DeepSeek的对比,探讨了模式匹配技术在不同领域的应用与相似性。从五子棋的棋局分析到自然语言处理,模式匹配构成了人工智能决策的核心机制。文章揭示了AI如何通过识别数据中的规律进行预测与生成,并展望了该技术在未来医疗、金融、自动驾驶等领域的广泛应用前景,展现了从简单游戏到智能世界的演进路径。
452 2
|
11月前
|
安全 Java Docker
Docker 部署 Java 应用实战指南与长尾优化方案
本文详细介绍了Docker容器化部署Java应用的最佳实践。首先阐述了采用多阶段构建和精简JRE的镜像优化技术,可将镜像体积减少60%。其次讲解了资源配置、健康检查、启动优化等容器化关键配置,并演示了Spring Boot微服务的多模块构建与Docker Compose编排方案。最后深入探讨了Kubernetes生产部署、监控日志集成、灰度发布策略以及性能调优和安全加固措施,为Java应用的容器化部署提供了完整的解决方案指南。文章还包含大量可落地的代码示例,涵盖从基础到高级的生产环境实践。
693 3
|
运维 Devops jenkins
十六年所思所感,聊聊这些年我所经历的 DevOps 系统
十六年所思所感,聊聊这些年我所经历的 DevOps 系统
346 2