【Java项目-企悦抽】07-用户与认证模块-实现用户注册01

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🎯 你正在阅读「Java项目-企悦抽」系列文章 🎯

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🔥 弹简特 个人主页

❄️ 个人专栏直通车:

靠热爱去书写自己,靠勇敢去书写生活!

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨


🌟 博主简介:


文章目录:


一、业务逻辑

1、时序图

那我们来看一下我们的本项目中哪些地方用到的注册功能。

1、当管理员在登录后台的时候,里面有一个注册账号,点击那个按钮,就跳转到我们的管理员注册页面。

2、与此同时,我们的管理员进入后台之后,有一个创建人员管理,人员管理里面有一个注册用户。

所以整篇我们涉及项目注册行为的就只有两个页面。

因此对于我们后台开发来说,这两个页面它涉及到的注册行为对于我们来说,可以整合为一个接口即可,一个方法,就可以提供这两个页面的注册需求。

二、准备阶段

1、管理员注册页面原型图

2、人员管理人员注册页面原型图


3、约定前后端交互的接口

请求地址/register

请求方式POST

请求类型application/json


3.1 请求体(Request Body)

json 复制代码
{
  "name": "张三",
  "mail": "451@qq.com",
  "phoneNumber": "13188888888",
  "password": "123456789",
  "identity": "ADMIN"
}

⚠️🎉为什么是这些字段🔗

这里你要知道,我们管理员注册的时候,这些字段都要包括,而我们管理员去注册普通用户的时候,无非就是少了一个密码这一个字段,与此同时,无论是注册管理员自己还是说创建一个普通用户,我们都用一个标识信息来标识自己的一个角色,所以我们多了一个identity就是我们的用户标识信息,你是admin那么就是我们的管理员,如果你是NORMAL那么就是我们的普通用户?

3.1.1 字段说明
  • name:用户名
  • mail:邮箱(登录账号)
  • phoneNumber:手机号
  • password:密码(建议后端加密存储)
  • identity:身份角色(可选:ADMIN / NORMAL

3.2 正常响应(Success Response)

json 复制代码
{
  "code": 200,
  "msg": "",
  "data": {
    "userId": 22
  }
}

⚠️

这里我们的响应为什么有这些字段的,其实是跟我们之前的设计那个统一返回结果类型是一致的,我们返回的三个字段就是我们的状态码。状态码对应的消息,以及我们的数据。

3.2.1 字段说明
  • code:状态码(200=成功)
  • msg:成功就直接返回空就行了
  • data.userId:新注册用户的唯一ID

3.3 失败响应示例

json 复制代码
{
  "code": 400,
  "msg": "写入你自己的失败信息",
  "data": null
}

三、注册实现-控制层+服务层代码

1、返回结果实体类

解释序列化

解释相应字段

2、注册参数实体类

2.1 序列化解释

首先我们注册的时候,希望这个请求呢,把这个JSON对象先进行一个序列化,再进行传递。

那对于后端来说,我们接受的是一个JSON对象。然后我们需要把这个JSON对象呢,转换为我们的一个java对象,所以呢我们得使用呢 Requestbody...注解。

3、控制层

使用的是post请求


方法的第一行我们一般都是进行日志的打印

我们希望打印之后的是序列化之后的JSON字符串


4、服务层

那么这里我们需要调用service层的方法 来实现我们的业务逻辑,因为

我们说过我们的Controller层是不应该有业务逻辑的,只是和前端打交道而已

4.1 服务层实体类DTO

这个实体类中为什么返回的是userid

为什么在后面加DTO?🤔

是我们的开发规范

Service向外传输的时候都是使用xxxDTO,例如:


你可以观察到 我们的UserService是一个接口(目的就是为了解耦),所以我们还得实现一个实现类才行

OK,那么我们先随便写一个测试数据 然后 我们再回到我们的控制层,等实现完毕控制层我们再来处理服务层

5、控制层


⚠️ 转换返回结果类型,引出控制层异常的完善

继续完善

继续完善


此时我们就测试一下:

成功的 只需要修改一下小细节

最终的结果:

6、注册参数校验

但是我们有一个需求性的问题,看下图:

即我们的姓名邮箱手机号都是必填的,只是密码不是必填的

对这些参数的验证,其实我们只需要使用一个注解就可以搞定了,只不过这个注解需要我们引入一些依赖:

xml 复制代码
        <!--spring-boot 2.3及以上的版本只需要引⼊下⾯的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

怎么使用?加@Validated注解只是标明我们的UserRegisterParam对象中的参数成员需要验证

然后具体的参数验证我们得去UserRegisterParam的属性中去


代码

java 复制代码
//控制层
@RestController
public class UserController {

    //日志对象:由于我们已经在spring-log.xml中已经配置了日志相关的代码 所以我们这里就直接使用
    //这里的Logger是我们的slf4j里面的logger LoggerFactory.getLogger(UserController.class);:注意在哪一个日志里面使用对象的打印 我们就使用哪一个对象
    /**
     * 当然 我们的日志 其实也是可以使用注解是完成的,而且会相对简单一点,我们博客系统中使用的是注解,那么在这里我们就使用xml吧
     */
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);


    @Autowired
    private UserService userService; //注入我们的service层

    /**
     * 注册方法
     */
    @PostMapping("/register")
    public CommonResult<UserRegisterResult> userRegister(
            @Validated @RequestBody UserRegisterParam userRegisterParam) {
        //入门第一行:日志打印:我们希望打印请求参数中的内容,目的就是将来我们调试接口的时候
        //就可以通过日志排查,那么排查我们就是看参数咯
        logger.info("用户注册接口userRegister被调用,请求参数userRegisterParam为:{}", JackSonUtil.writeValueAsString(userRegisterParam));

        //那么这里我们需要调用service层的方法 来实现我们的业务逻辑,因为
        //我们说过我们的Controller层是不应该有业务逻辑的,只是和前端打交道而已


        //调用Service层的服务 进行一个访问
        UserRegisterDTO userRegisterDTO = userService.userRegister(userRegisterParam);

        //需要将UserRegisterDTO转换为UserRegisterResult
        //所以直接
        return CommonResult.success(converToUserRegisterResult(userRegisterDTO));
    }

    /**
     * 将UserRegisterDTO转换为UserRegisterResult
     */
    private UserRegisterResult converToUserRegisterResult(UserRegisterDTO userRegisterDTO) {
        UserRegisterResult userRegisterResult = new UserRegisterResult();
        if (null == userRegisterDTO) { //如果我们的dto为空
            //那么之前我们定义了一个控制层的异常类,此时就发挥作用了
            throw new ControllerException(ControllerErrorCodeConstants.REGISTER_ERROR);
        }
        //程序到此处 就代表tdo不为空
        userRegisterResult.setUserId(userRegisterDTO.getUserId());
        return userRegisterResult;
    }
}

//实体类
/**
 * @ClassName UserRegisterParam
 * @Description TODO 用户注册参数
 * @Author zhongge
 * @Version 1.0
 */
@Data
public class UserRegisterParam implements Serializable{//实现这个Serializable的目的是为了 UserRegisterParam是在网络中传输的所以加这个接口实现,总之记住这个习惯就好
    /**
     * 前端传递的参数如下
     * {
     *   "name": "张三",
     *   "mail": "451@qq.com",
     *   "phoneNumber": "13188888888",
     *   "password": "123456789",
     *   "identity": "ADMIN"
     * }
     */
    @NotBlank(message = "姓名不能为空")//@NotBlank专门针对String不为空来说的
    private String name; //姓名

    @NotBlank(message = "邮箱不能为空")
    private String mail; //邮箱

    @NotBlank(message = "手机号不能为空")
    private String phoneNumber; //手机号

    private String password; //密码

    @NotBlank(message = "身份信息不能为空")
    private String identity; //身份信息
}

/**
 * @ClassName UserRegisterResult
 * @Description TODO 用户注册结果
 * @Author zhongge
 * @Version 1.0
 */
@Data
public class UserRegisterResult implements Serializable {
    //为什么需要有这个字段呢?这个是根据我们响应接口来的
    private Long userId;
}

//服务层的DTO
/**
 * @ClassName UserRegisterDTO
 * @Description TODO 用户注册DTO
 * @Author zhongge
 * @Version 1.0
 */
@Data
public class UserRegisterDTO {
    /**
     * 用户id
     */
    private Long UserId;
}

//控制层异常状态码的完善
/**
 * @ClassName ControllerErrorCodeConstants
 * @Description TODO 控制层的错误码常量
 * @Author zhongge
 * @Version 1.0
 */
public interface ControllerErrorCodeConstants {
    //TODO 具体有哪一些错误码 我们写到对应代码的时候再说

    //------人员模块的错误码: 都已100开头--------
    ErrorCode REGISTER_ERROR = new ErrorCode(100, "注册失败");//注册错误状态码

    //------活动模块的错误码--------


    //------奖品模块的错误码--------



    //------抽奖的错误码--------
}

7、注册服务层实现-校验基本参数

对于服务层我们使用的 是 接口 与实现 分离的方式,他是Java编程所推崇的一种设计哲学

我们先校验注册信息,但是校验注册信息的时候,有老铁说我们的控制层中不是已经校验过了吗?

你看👇

为何此处还要校验呢?

除此之外,我们其实还要再校验一些信息,比如我们校验邮箱的格式,电话的格式,对于他的密码啊我们校验一下这个密码是否填了。对于我们管理员说,密码是必填的,而对于普通用户来说,密码是可以不用填的,这些都是需要我们手动的校验。

再比如我们的用户身份信息管理员传入的是我们的管理员身份,普通用户传入的是普通用户身份,就只有这两个身份,没有其他的身份,所以这些都需要我去处理。

那么在此处你会发现:我们的抛出的异常是我们之前定义的服务层异常,所以我们就引出了对这个异常的完善

7.1 完善服务层异常

那么继续:邮箱对于手机号以及密码的位数,我们使用正则表达式来校验,

我们直接去复制就行,不用自己写。

为了方便,我们把这些东西封装成一个工具类。RegexUtil类

代码:看懂就行

java 复制代码
import org.springframework.util.StringUtils;
import java.util.regex.Pattern;

public class RegexUtil {
    /**
     * 邮箱:xxx@xx.xxx(形如:abc@qq.com)
     *
     * @param content
     * @return
     */
    public static boolean checkMail(String content) {
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表⽰匹配字符串的开始。
         * [a-z0-9]+ 表⽰匹配⼀个或多个⼩写字⺟或数字。
         * ([._\\-]*[a-z0-9])* 表⽰匹配零次或多次下述模式:⼀个点、下划线、反斜杠或短
         横线,后⾯跟着⼀个或多个⼩写字⺟或数字。这部分是可选的,并且可以重复出现。
         * @ 字符字⾯量,表⽰电⼦邮件地址中必须包含的"@"符号。
         * ([a-z0-9]+[-a-z0-9]*[a-z0-9]+.) 表⽰匹配⼀个或多个⼩写字⺟或数字,后⾯可以
         跟着零个或多个短横线或⼩写字⺟和数字,然后是⼀个⼩写字⺟或数字,最后是⼀个点。这是匹配域名
         的⼀部分。
         * {1,63} 表⽰前⾯的模式重复1到63次,这是对顶级域名⻓度的限制。
         * [a-z0-9]+ 表⽰匹配⼀个或多个⼩写字⺟或数字,这是顶级域名的开始部分。
         * $ 表⽰匹配字符串的结束。
         */
        String regex = "^[a-z0-9]+([._\\\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$";

        return Pattern.matches(regex, content);
    }

    /**
     * ⼿机号码以1开头的11位数字
     *
     * @param content
     * @return
     */
    public static boolean checkMobile(String content) {
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表⽰匹配字符串的开始。
         * 1 表⽰⼿机号码以数字1开头。
         * [3|4|5|6|7|8|9] 表⽰接下来的数字是3到9之间的任意⼀个数字。这是中国⼤陆⼿机号
         码的第⼆位数字,通常⽤来区分不同的运营商。
         * [0-9]{9} 表⽰后⾯跟着9个0到9之间的任意数字,这代表⼿机号码的剩余部分。
         * $ 表⽰匹配字符串的结束。
         */
        String regex = "^1[3|4|5|6|7|8|9][0-9]{9}$";
        return Pattern.matches(regex, content);
    }

    /**
     * 密码强度正则,6到12位
     *
     * @param content
     * @return
     */
    public static boolean checkPassword(String content) {
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表⽰匹配字符串的开始。
         * [0-9A-Za-z] 表⽰匹配的字符可以是:
         * 0-9:任意⼀个数字(0到9)。
         * A-Z:任意⼀个⼤写字⺟(从A到Z)。
         * a-z:任意⼀个⼩写字⺟(从a到z)。
         * {6,12} 表⽰前⾯的字符集合(数字、⼤写字⺟和⼩写字⺟)可以重复出现6到12次。
         * $ 表⽰匹配字符串的结束。
         */
        String regex = "^[0-9A-Za-z]{6,12}$";
        return Pattern.matches(regex, content);
    }
}

ok 然后完善我们的服务层状态码

java 复制代码
public interface ServiceErrorCodeConstants {
    //TODO 具体有哪一些错误码 我们写到对应代码的时候再说

    //------人员模块的错误码--------
    ErrorCode REGISTER_INFO_IF_EMPTY = new ErrorCode(100, "注册信息为空");
    ErrorCode MAIN_ERROR = new ErrorCode(101, "邮箱错误");
    ErrorCode PHONE_NUMBER_ERROR = new ErrorCode(102, "手机号错误");
    ErrorCode IDENTITY_ERROR = new ErrorCode(103, "身份错误");
    ErrorCode PASSWORD_IS_EMPTY = new ErrorCode(104, "身份错误");
    ErrorCode PASSWORD_ERROR = new ErrorCode(105, "身份错误");
    //------活动模块的错误码--------


    //------奖品模块的错误码--------



    //------抽奖的错误码--------
}

我们邮箱和手机号校验完毕

想要校验用户的身份信息,我们可以维护一个枚举类型,在枚举类里面,我们再维护一个普通用户身份和管理员身份,然后再去比对前端发起的这个请求参数中,我们所维护的身份信息是否在里面,如果不在,我们就抛一个异常。

维护的枚举类如下:

java 复制代码
@Getter
public enum UserIdentityEnum {
    //管理员
    ADMIN("管理员"),
    //普通用户
    NORMAL("普通用户");

    //维护一个属性标记他们
    private String message;

    //维护一个构造方法
    UserIdentityEnum(String message) {
        this.message = message;
    }
    
    public static UserIdentityEnum forName(String name){
        for(UserIdentityEnum userIdentityEnum : UserIdentityEnum.values()){
            if(userIdentityEnum.getMessage().equals(name)){
                return userIdentityEnum;
            }
        }
        return null;
    }
}

继续实现校验用户身份

校验管理员密码必填

密码强度的校验 以及 逻辑梳理如下👇

代码

java 复制代码
//接口
/**
 * @InterfaceName UserService
 * @Description TODO 用户服务接口
 * @Author zhongge
 * @Version 1.0
 */
public interface UserService {
    /**
     * 注册
     */
    UserRegisterDTO userRegister(UserRegisterParam userRegisterParam);
}

//实现类
/**
 * @ClassName UserServiceImpl
 * @Description TODO
 * @Author zhongge
 * @Version 1.0
 */
@Service
public class UserServiceImpl implements UserService {
    @Override
    public UserRegisterDTO userRegister(UserRegisterParam userRegisterParam) {

        //校验用户注册信息
        checkRegisterInfo(userRegisterParam);

        //加密私密数据 (构造dao层的请求)

        //保存数据

        //构造返回的数据


        //TODO 我们先暂时在这里写一个假数据 等我们把 Controller写完毕 再来写这一层吧

        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();

        userRegisterDTO.setUserId(1L);

        return userRegisterDTO;
    }

    private void checkRegisterInfo(UserRegisterParam userRegisterParam) {
        //首先校验入参否是空的
        if(null == userRegisterParam) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IF_EMPTY);
        }

        /**
         * 邮箱对于手机号以及密码的位数,我们使用正则表达式来校验,
         * 我们直接去复制就行,不用自己写。
         */
        //校验邮箱格式: xxx@xxx.xxx
        if (!RegexUtil.checkMail(userRegisterParam.getMail())) {
            //校验不通过就直接抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.MAIN_ERROR);
        }

        //校验手机号格式
        if (!RegexUtil.checkMobile(userRegisterParam.getPassword())) {
            //校验不通过就直接抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        //校验身份信息:我们可以维护枚举信息
        if (null == UserIdentityEnum.forName(userRegisterParam.getIdentity())) {
            //说明用户身份不在"管理员"和"普通用户"中
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        //校验管理员密码必填
        if (userRegisterParam.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && !StringUtils.hasText(userRegisterParam.getPassword())) {
            //当前是管理员 且 没有进行密码的填写 就抛异常
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }

        //密码校验,至少是6位
        if (StringUtils.hasText(userRegisterParam.getPassword())
                && !RegexUtil.checkPassword(userRegisterParam.getPassword())) {
            //只有当他是管理员才会传递密码 然后只有传递密码 我们才会对他进行密码强度的校验

            //程序进入这里 代表 他是管理员了,但是他的密码强度没有通过
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        //校验邮箱是否被使用
        if (checkMailUsed(userRegisterParam.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED_ERROR);
        }

        //校验手机号是否被使用
    }

    private boolean checkMailUsed(String mail) {
        //判断邮箱是否被使用,其实就是直接去查数据库表
        return false;
    }
}

最后校验邮箱和手机号是否被使用:请继续向下看👇

8、注册实现-校验邮箱

判断邮箱是否被使用,其实就是直接去查数据库表,此时就是引入我们的Mybatis框架了

xml 复制代码
        <!-- Mybatis 依赖包 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

进行一些配置项的配置

bash 复制代码
## MySql ##
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/lottery_system?characterEncoding=utf8&useSSL=false
#连接数据库的⽤⼾名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123456

## MyBatis ##
#配置mybatis xml的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
# 如果你使用的是xml那么就使用此种配置 我们本次使用的是注解的方式完成Mybatis的sql的编写
#mybatis.mapper-locations=classpath:mapper/*Mapper.xml

#配置驼峰⾃动转换
mybatis.configuration.map-underscore-to-camel-case=true

创建用户持久层: //注意 我们只写接口 具体的实现类已经有底层给我们自己实现了

继续实现服务层

然后我们可以在测试类中进行测试

java 复制代码
@SpringBootTest
public class SqlTest {

    @Autowired
    private UserMapper userMapper;


    @Test
    void mailCount() {
        //其实核心目的就是验证sql是否报错而已
        int count = userMapper.countByMail("123456789@qq.com");
        System.out.println(count);
    }
}

ok 到此我们的用户注册实现的第一部分就先到这里,后续实现用户注册内容的第二部分请参考:08-用户与认证模块-实现用户注册02


四、附录:补充枚举类的使用

java 复制代码
package com.zhongge.lotterysystem.service.enums;

import lombok.Getter;

/**
 * @ClassName UserIdentityEnum
 * @Description TODO
 * @Author zhongge
 * @Version 1.0
 */
@Getter
public enum UserIdentityEnum {
    //管理员
    ADMIN("管理员"),
    //普通用户
    NORMAL("普通用户");

    //维护一个属性标记他们
    private String message;

    //维护一个构造方法
    UserIdentityEnum(String message) {
        this.message = message;
    }
}

1、先搞懂:枚举到底是什么?(概念)

生活例子

你现在做的用户身份,只有两种

  1. 管理员
  2. 普通用户

以后永远不会多出第三种,也不会少掉,固定死的几个选项。

这种固定、有限、不会随便新增 的选项,Java 专门给了一个语法,就叫 枚举(enum)

对比以前会的写法,就瞬间懂了

以前我们写身份,是不是用字符串?

java 复制代码
String identity = "管理员";
String identity = "普通用户";

缺点一大堆:

  • 手一抖写成 管理元管里员,代码不报错
  • 不知道一共有多少种身份
  • 到处写字符串,乱得很

枚举就是用来替代这种固定字符串的!

一句话总结概念:

枚举 = 自己定义的、固定的、有限的常量集合

就像一个固定菜单,里面菜是定死的,只能选里面的,不能乱编。

2、结合完整代码,逐字逐句讲语法

java 复制代码
package com.zhongge.lotterysystem.service.enums;

import lombok.Getter;

@Getter
public enum UserIdentityEnum {
    //管理员
    ADMIN("管理员"),
    //普通用户
    NORMAL("普通用户");

    //维护一个属性标记他们
    private String message;

    //维护一个构造方法
    UserIdentityEnum(String message) {
        this.message = message;
    }
}

2.1 public enum UserIdentityEnum

我们一起写类的关键字是 class

java 复制代码
public class XXX

现在是 enum

意思:我现在不定义普通类,我定义一个枚举类 ,专门装固定选项。

类名:UserIdentityEnum 用户身份枚举

2.2 最核心:枚举常量(整个枚举最重要的部分)

java 复制代码
ADMIN("管理员"),
NORMAL("普通用户");

这两行,就是枚举全部的灵魂

我给你翻译成人话:

  • ADMIN代码里用的代号(英文大写,程序员内部用)
  • ("管理员"):这个代号对应的中文含义,给人看的
  • 多个选项之间用 逗号 , 隔开
  • 所有选项写完,最后加一个 分号 ; 结束

语法死规则:

  1. 所有枚举常量,必须写在整个类的最最最上面
  2. 名字全部大写,下划线分隔(行业规范)
  3. 逗号分隔,最后一个结尾必须分号

你可以把它理解成:

复制代码
ADMIN  对应  管理员
NORMAL 对应  普通用户

2.3 private String message;

这是成员变量

你括号里传了中文,总得有个地方存起来吧?

这个 message 就是仓库,专门存放每个枚举对应的中文文字。

2.4 @Getter 注解

我们用了Lombok的@Getter

作用:自动帮你生成 getMessage() 方法

你不用自己手写:

java 复制代码
public String getMessage(){
    return this.message;
}

有了它,外面代码就能拿到存好的中文。

2.5 构造方法

java 复制代码
UserIdentityEnum(String message) {
    this.message = message;
}

先回忆普通类的构造方法:

new 对象(参数),给对象属性赋值。

枚举的构造方法非常特殊!

  1. 你永远不能手动 new 枚举
    你写不了 new UserIdentityEnum("管理员") 这种代码,编译器直接报错。
  2. 这个构造方法,只有枚举自己内部能用
    就是专门给上面的 ADMIN("管理员")NORMAL("普通用户") 用的!

执行过程你一看就懂:

  1. 读到 ADMIN("管理员")
  2. 自动调用这个构造方法
  3. "管理员" 传给形参 message
  4. this.message = message; 赋值完成
    也就是:ADMIN 这个常量,内部存好了 message=管理员

同理:

NORMAL("普通用户") → 内部 message=普通用户

3、枚举底层本质

我们写的:

java 复制代码
ADMIN("管理员"),
NORMAL("普通用户");

本质上是什么?

每一个大写单词,都是当前枚举类的一个 静态常量对象

拆开讲:

ADMIN 本质 =

public static final UserIdentityEnum ADMIN = new UserIdentityEnum("管理员");

NORMAL 本质 =

public static final UserIdentityEnum NORMAL = new UserIdentityEnum("普通用户");

只是 Java 给你简化语法了,不用你写这么长。

所以:

整个程序运行中,ADMIN 对象永远只有一个,NORMAL 也永远只有一个,不会重复创建

4、最基础语法全部总结

以后写所有枚举,全部固定模板,一模一样结构:

java 复制代码
@Getter
public enum 枚举类名 {
    //1. 先写所有常量,逗号分隔,最后分号
    常量1("描述1"),
    常量2("描述2");

    //2. 定义成员变量,存描述
    private String 变量名;

    //3. 构造方法,给变量赋值
    枚举类名(String 变量名) {
        this.变量名 = 变量名;
    }
}

这就是Java 带属性枚举的万能模板,99%业务代码都长这样。

5、到底怎么用?

java 复制代码
//1. 获取枚举对应的中文
String adminMsg = UserIdentityEnum.ADMIN.getMessage();
System.out.println(adminMsg); // 输出:管理员

String normalMsg = UserIdentityEnum.NORMAL.getMessage();
System.out.println(normalMsg); // 输出:普通用户
java 复制代码
//2. 判断身份(开发最常用!)
//假设从数据库查到用户身份是枚举类型
UserIdentityEnum identity = UserIdentityEnum.ADMIN;

// 枚举判断 直接用 == 就行!!!
if(identity == UserIdentityEnum.ADMIN){
    System.out.println("这是管理员,拥有所有权限");
}else if(identity == UserIdentityEnum.NORMAL){
    System.out.println("这是普通用户,普通权限");
}

6、把所有概念浓缩成你能一辈子记住的大白话

  1. 枚举是什么?
    固定不变、数量有限的选项集合,用来替代硬编码字符串。
  2. ADMIN、NORMAL 是什么?
    就是一个个固定好的对象,程序里全局唯一。
  3. 括号里的中文干嘛的?
    附带的描述信息。
  4. message 变量干嘛的?
    存放这个中文。
  5. 构造方法干嘛的?
    创建对象时,自动把中文存进 message,你不用手动调用
  6. @Getter 干嘛的?
    对外提供 get 方法,让你能取出里面的中文。
  7. 使用规则
    取值用 枚举.常量.getMessage()
    判断相等直接用 ==,不用 equals!

7、解决所有疑惑盲区

  1. 为什么构造方法不写 public?
    枚举构造方法天生私有,禁止外部 new 对象,保证只能用内部固定的那几个。
  2. 为什么常量必须写最上面?
    语法强制规定,枚举常量必须放在类体第一行。
  3. 为什么要用枚举,不用字符串?
  • 不会写错字
  • 代码有提示,IDEA 自动补全
  • 类型安全,不会乱传奇怪字符串
  • 统一管理所有选项,好找好改
  1. 为什么要用 message 存中文,不直接用英文?
    代码里用英文 ADMIN,数据库存英文;返回前端、展示页面、打印日志用中文。

最后,如果这篇文章对你有用,那么点赞收藏、评论区逛一逛我们下一篇继续实现用户注册功能👋~

相关推荐
山东点狮信息科技有限公司1 小时前
SpringBoot+VUE3,一套系统完成OA+HRM+CRM+MES+合同+项目管理 —— 深度解析点狮全业务管理平台
java·spring boot·后端
你怎么知道我是队长1 小时前
JavaScript 的控制语句介绍
开发语言·javascript·ecmascript
RSTJ_16252 小时前
PYTHON+AI LLM DAY ONE HUNDRED AND THREE
开发语言·人工智能·python
snow@li2 小时前
Java:Java 服务器(Web 容器 / Servlet 容器)完整工作清单
java·服务器·前端
青山木2 小时前
Hot 100 --- 二叉树与递归
java·数据结构·算法·leetcode·深度优先
charlie1145141913 小时前
RK3506B: buildroot:出一份正规的最小 rootfs
开发语言·嵌入式·开源项目·rk3506b
jason成都3 小时前
GNSS-QC 数据清洗功能全面增强|滑坡监测实测验证,适配 rtklib_java 高精度解算链路
java·gnss
前端工作日常3 小时前
我学习到的Java概念
java·后端
卓怡学长3 小时前
w263基于springboot + vue 健康饮食管理系统
java·数据库·vue.js·spring boot·spring·intellij-idea