【Java基础】消灭魔法值-常量枚举详述

news2024/9/28 3:26:12

🚩 本文已收录至专栏:JAVA基础
👍希望能对你有所帮助

一.引入

在开发中我们经常会遇到用一个值(诸如数字或字符串)表示一种状态的情况,例如:

  • 用 1 表示男,用 0 表示女
  • 用 0 表示状态正常,用 1 表示异常,用 2 表示封禁
  • 用 admin 表示管理员,用 common 表示普通用户

如果直接在代码中书写这些数值将大大破坏代码的可维护性,即使本人亲自编写的代码,在一段时间没接触之后,需要重新上手都得重新翻阅以前写的批注,如果没有写又不记得…更何况有些时候代码都不是自己所编写的…

阿里巴巴Java开发规范手册中有提到:**不允许任何魔法值(即未经定义的常量)直接出现在代码中。**为了提高我们代码的可维护性,这也是许多人都遵从的一点。

什么是魔法值呢?

它通常是指在代码编写时莫名出现的数字或字符串,我们无法直接判断数值代表的含义,必须通过联系代码上下文分析才可以明白,严重降低了代码的可读性。结合我们上述所举的例子:

// 不推荐
gender.setGender(1); // 设置性别为男
// 这里所使用的 1 就是一个魔法值

除此之外,如果在代码中使用了大量的魔法值,假设我们需要对其值进行修改那么工作量也是极其巨大的而且极易遗漏。

为了解决这个问题,我们可以采用如下两种方式来做信息标志和分类:

  • 常量
  • 枚举

二.常量

我们可以先声明一个变量并且赋值,然后再使用,但是这样做用户可以随意的修改变量值,导致安全性大大降低。由此我们可以选择使用常量。

(1) 语法

  • 定义:使用了public static final修饰的成员变量必须有初始化值,一旦初始化后,在执行的过程中其值不能再被改变

  • 命名规范:通常使用英文单词全部大写多个单词下划线连接起来。

// 使用示例,也可以写成接口形式
// 标识用户状态常量
public class UserStatus {
    // 0 -》 正常 
    public static final Integer USER_NORMAL  = 0;
    // 1 -》 异常
    public static final Integer USER_ABNORMAL  = 1;
    // 2 -》 禁止
    public static final Integer USER_PROHIBIT  = 2;
}

类似于我们正常声明成员变量,只不过为了保证其不可修改的特性,加上了public static final修饰。

补充说明

在我们使用常量的时候,Java会在编译阶段自动进行“宏替换”,也就是把使用常量的地方全部替换成真实的字面量,提高一点系统的性能。

// 比如我们使用常量 UserStatus.USER_NORMAL
System.out.println(UserStatus.USER_NORMAL);
// java会在编译阶段替换为字面量 0
System.out.println(0);

(2) 去魔法化

假设我们需要将用户的状态设置为异常状态,

以前我们直接使用数字,并加上注释(可能有些人注释都懒得加):

// 设置用户状态为异常
user.setStatus(1); // 不推荐

在一个规模比较小或者使用处极少的项目中可能问题不大,但如果项目规模比较庞大,开发时间跨度大,使用处比较多,那么上述写法便非常的不友好,由此我们可以使用上述所学的常量。

常量代替:

// 还是使用我们语法介绍部分声明的常量
// 使用变量代替魔法值,设置用户状态为异常
user.setStatus(UserStatus.USER_ABNORMAL); // 推荐√

可能会有人觉得这样写也太长了,但是对于一个有代码提示的IDE来说其实并不算什么问题,更何况它的可读性明显提高了。当我们需要修改指代状态数字时,仅仅需要在UserStatus类中修改一次即可处处生效。

三.枚举

虽然常量也可以达成目的,但在一些约束性比较强的情况下,枚举类更加的适合。例如,你期待接收已定义的常量数值,事实上程序可以接受任意符合参数类型的数值。

(1) 语法

枚举是Java中的一种特殊类型,需要先创建一个枚举类再进行使用。

语法格式

修饰符 enum 枚举名称{
     // 罗列枚举类实例的名称,以逗号(,)分隔实例,以分号(;)标识结尾
}
  • 示例一:

    • 对于一些简单的场景,我们可以直接使用枚举示例:
    // ,分割实例 ;标识结尾
    public enum SeasonEnum {
        // 上下左右
        UP, 
        DOWN, 
        LEFT, 
        RIGHT;
    }
    
    • 调用与使用抽离到单独类中的常量类似
    public class App {
        public static void main(String[] args) {
            System.out.println(SeasonEnum.UP);
            System.out.println(SeasonEnum.DOWN);
            System.out.println(SeasonEnum.LEFT);
            System.out.println(SeasonEnum.RIGHT);
        }
    }
    
    • 可以看到除了枚举类内置的方法以外,我们只能使用事先罗列的枚举类实例。
  • 示例二:

    • 对于一些稍复杂的情况,我们只使用实例仅仅能完成标识,无法完成存储等用途。由此我们还可以为每个实例增加一个至多个值

      public enum UserStatusEnum {
          // 根据构造器枚举实例,此时不含无参构造器,必须要赋值枚举
          NORMAL(0),
          ABNORMAL(1),
          PROHIBIT(2);
      
          // 必须提供对应参数构造器
          UserStatusEnum(Integer value) {
              this.value = value;
          }
          
      	// 不建议给用户提供二次修改枚举值的权限
          // 可以有多个字段
          private final Integer value;
      
          // 仅开放获取实例值权限
          public Integer getValue() {
              return value;
          }
      }
      
    • 使用上与上述类似,不过多了些许自定义方法,例如:getValue

    public class App {
        public static void main(String[] args) {
            // 可以仅获取枚举值
            System.out.println(UserStatusEnum.NORMAL); // NORMAL
            // 还可以获取枚举值所指代的数值
            System.out.println(UserStatusEnum.NORMAL.getValue()); // 0
        }
    }
    
    • 可以看到与上述类似,不过多了我们提供的获取值方法
      在这里插入图片描述
      在这里插入图片描述

(2) 特点

  • 枚举类都是继承了枚举类型:java.lang.Enum
  • 枚举都是最终类,不可以被继承
  • 构造器都是私有的,枚举对外不能通过new创建对象
  • 枚举类的第一行默认都是罗列枚举对象的名称的。
  • 枚举类相当于是多例模式

我们可以反编译后结合上述观察枚举的特征:
在这里插入图片描述

(3) 去魔法化

假设我们同样需要将用户的状态设置为异常状态:

// 设置用户状态为异常
user.setStatus(1); // 不推荐
// 使用常量
user.setStatus(UserStatus.USER_ABNORMAL); // 推荐一√

我们也可以使用上述所学的枚举值进行代替

// 还是使用我们语法介绍部分声明的枚举值
// 使用枚举值代替魔法值,设置用户状态为异常
user.setStatus(UserStatusEnum.ABNORMAL.getValue()); // 推荐二√

可能有人会发现这样写其实和使用没啥区别,如果仅仅是这样用确实没什么区别。但是在方法参数列表中,让枚举类当作参数那便有很强的约束性。

public class App {
    public static void main(String[] args) {
        setStatus(UserStatusEnum.NORMAL);
    }

    public static void setStatus(UserStatusEnum userStatus){
        User user = new User();
        user.setStatus(userStatus.getValue());
    }
}

这样一来我们要想设置用户状态便只能传递枚举类中已经存在的几个枚举实例。

此外对于一些方法只能接受数值的情况,我们还先通过values获取所有值进行判断数值正确性,再进行处理。

public class App {
    public static void main(String[] args) {
        setStatus(1);

    }

    // 假设只能接收非枚举实例类型
    public static void setStatus(Integer status) {
        // 判断参数是否为对于枚举实例
        if (isTrueValue(status)) {
            User user = new User();
            user.setStatus(status);
        } else {1
            System.out.println("非法参数");
        }

    }

    // 判断参数是否为对于枚举实例
    public static boolean isTrueValue(Integer num) {
        UserStatusEnum[] values = UserStatusEnum.values();
        for (UserStatusEnum statusEnum : values) {
            Integer value = statusEnum.getValue();
            if (value.equals(num)) {
                return true;
            }
        }
        return false;
    }
}

虽说使用常量结合哈希也能解决上述问题,但这终究需要因情况决定用法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/438825.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

被裁员了,要求公司足额补缴全部公积金,一次补了二十多万!网友兴奋了,该怎么操作?...

被裁员后,能要求公司补缴公积金吗? 一位网友问: 被裁员了,要求公司把历史公积金全部足额缴纳,现在月薪2.3万,但公司每个月只给自己缴纳300元公积金,结果一次补了二十多万,一次性取出…

步进频雷达信号脉冲压缩仿真matlab

步进频雷达信号脉冲压缩仿真matlab 发射与回波信号模型仿真分析 发射与回波信号模型 步进频率信号发射得的是一串窄带的相参脉冲,每个脉冲的载频之间是均匀线性步进的,经过相参本振对目标回波信号进行混频、采样,再对同距离门的采样值做逆傅里…

java 入门Spring,SpringBoot,SpringCloud等技术全栈教程网站导航

我们整理了Java的全栈技术路线的各个精品网站,从最简单的基础语法,到核心JVM,到企业级开发Spring以及通信技术Netty等开发,每个网站都有对应的免费的精品教程。我们全部收录到 java 技术全栈学习导航http://​www.webhub123.com/…

高级服务框架(黑马)

一、修改order-service服务 修改OrderService&#xff0c;让其监听Nacos中的sentinel规则配置。 具体步骤如下&#xff1a; 1.引入依赖 在order-service中引入sentinel监听nacos的依赖&#xff1a; <dependency><groupId>com.alibaba.csp</groupId><…

电子专业英语(持续更新)

前言 23考研刚刚结束&#xff0c;但在我复试的两个学校的专业英语翻译我都不会。发现自己在专业英语方面很差&#xff0c;故写此文章记录我专业英语的学习历程 2023/4/17日学习 Analog /ˈnəlɒɡ/ signal 模拟信号 Continuous values 连续值…

怎样远程控制另一台电脑

有多种方法可以远程控制另一台计算机&#xff0c;您选择的方法将取决于您的具体需要和涉及的操作系统。以下是您可以遵循的一些一般步骤&#xff1a; 在目标计算机上启用远程访问&#xff1a;在 Windows 上&#xff0c;您可以通过转到“控制面板”>“系统和安全”>“系统…

DFIG控制10: 双馈发电机的动态模型

DFIG控制10&#xff1a; 双馈发电机的动态模型。主要介绍DFIG在三相坐标系、定子αβ坐标系、dq同步坐标系下的模型。 本文主要是整理了DFIG的动态模型的公式和坐标变换的过程。某些描述是为了便于自己理解&#xff0c;不一定准确。 大部分内容参考&#xff1a; G. Abad, J. …

Windows逆向安全(一)之基础知识(十二)

结构体 C语言中的结构体是一种自定义的数据类型&#xff0c;一个结构体里可由其它各种类型组合而成 声明结构体 举个简单的例子&#xff0c;自定义一个为player的类型&#xff0c;如下&#xff1a; struct Player{float hp; //人物血量float mp; …

MongoDB 聚合管道中使用数组表达式运算符判断数组中是否包含元素($in)并获取元素索引($indexOfArray)

数组表达式运算符主要用于文档中数组的操作&#xff0c;之前我们介绍了如果获取文档数组中的元素&#xff1a; MongoDB 聚合管道中使用数组表达式运算符获取数组中指定位置的元素($arrayElemAt&#xff0c;$first&#xff0c;$last)https://blog.csdn.net/m1729339749/article…

k8s 1.20 二进制安装

安装前的准备 CentOS Linux release 7.3.1611 (Core) 初始化系统参数 集群规划 192.168.6.20 master1 - etcd、kube-apiserver、kube-controller-manager、kube-scheduler 192.168.6.21 master2 - etcd、kube-apiserver、kube-controller-manager、kube-scheduler 192.168.6…

手牵手SpringBoot2集成Redis7

Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 Redis是一个NoSQL数据库&#xff0c;常用缓存(cache) Re…

注册苹果开发者账号的方法

在2020年以前&#xff0c;注册苹果开发者账号后&#xff0c;就可以生成证书。 但2020年后&#xff0c;因为注册苹果开发者账号需要使用Apple Developer app注册开发者账号&#xff0c;所以需要缴费才能创建ios证书了。 所以新政策出来后&#xff0c;注册苹果开发者账号&#…

2023年2月安全更新补丁

一、2023年2月安全更新补丁 近日&#xff0c;微软发布了2023年2月安全更新补丁&#xff0c;共发布了75个漏洞的补丁程序&#xff0c;其中18个漏洞值得关注&#xff08;包括个7严重漏洞、11个重要漏洞&#xff09; Microsoft Windows是美国微软公司以图形用户界面为基础研发的…

SSM学习记录7:通过cdn引入vue进行使用

通过cdn引入vue进行使用 引入vue 和 vue-router&#xff0c;注&#xff1a;vue的版本要比router版本低一个版本&#xff0c;例vue2配router3 <!-- import Vue before Element --><script src"https://cdn.jsdelivr.net/npm/vue2.7.14"></script>&…

CloudEon云原生大数据平台

文章目录 [toc] 1. CloudEon是什么&#xff1f;2. gitHub地址和官网地址3. 官网教程4. 特性5. 架构6. 支持组件版本7. 安装部署7.1 部署前提7.1.1 Kubernetes环境准备&#xff08;必须&#xff09;7.1.2 SSH服务准备&#xff08;必须&#xff09;7.1.3 数据库环境准备&#xff…

【学习笔记】websocket数据帧

介绍 RFC官网给出的数据帧格式 字段解释 FIN&#xff08;1bit&#xff09;: 标记位&#xff0c;表示该数据帧是否为完整消息最后的数据帧。RSV1/2/3&#xff08;各1bit&#xff09;: 标记位&#xff0c;根据RFC的介绍&#xff0c;这三个bit位是用做扩展用途&#xff0c;没有…

rslidar_SDK二次开发

rslidar_SDK代码二开注意事项 文章目录 rslidar_SDK代码二开注意事项0.下载地址1.CMakeLists.txt1.1 Cmake里面&#xff0c;首先选择编译方式&#xff0c;原始ORIGINAL还是CATKIN&#xff0c;还是COLCON1.2 增加新增cpp文件位置1.3 如果选择ORIGINAL模式&#xff0c;需要额外增…

LabView中数组的使用2-1

在LabView中&#xff0c;数组用来管理相同类型的数据。 1 在前面板中创建数组 1.1 创建空数组 在前面板中创建数组时&#xff0c;首先在前面板中点击鼠标右键&#xff0c;弹出“控件”对话框&#xff0c;之后选择“新式->数组、矩阵与簇->数组”&#xff0c;在前面板中…

硬盘格式化工具,强烈推荐这个!

案例&#xff1a;硬盘格式化工具推荐 【我的电脑已经用了好几年了&#xff0c;硬盘存储容量严重不够了&#xff0c;最近想把它格式化&#xff0c;但却不知道怎么操作&#xff0c;大家有什么比较好的硬盘格式化工具可以推荐吗&#xff1f;】 硬盘作为存储设备&#xff0c;我们…

薄膜热电化学电池性能测试中的半导体制冷片高精度度温度控制解决方案

摘要&#xff1a;电化学热电池&#xff08;electrochemical thermcells&#xff09;作为用于低品质热源的热电转换技术&#xff0c;是目前可穿戴电子产品的研究热点之一&#xff0c;使用中要求具有一定的温差环境。电化学热电池相应的性能测试就对温度和温差形成提出很高要求&a…