✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🎯 你正在阅读「Java项目-企悦抽」系列文章 🎯
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🔥 弹简特 个人主页
❄️ 个人专栏直通车:
- 🔌 接口测试从入门到跑路
- ☕ 一个后端的 JavaEE 续命指南
- 🛜 网络原理续命手册
- ☕ Java项目-轻聊
✨ 靠热爱去书写自己,靠勇敢去书写生活!
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🌟 博主简介:

文章目录:
- 一、业务逻辑
- 二、准备阶段
-
- 1、管理员注册页面原型图
- 2、人员管理人员注册页面原型图
- 3、约定前后端交互的接口
-
- [3.1 请求体(Request Body)](#3.1 请求体(Request Body))
-
- [3.1.1 字段说明](#3.1.1 字段说明)
- [3.2 正常响应(Success Response)](#3.2 正常响应(Success Response))
-
- [3.2.1 字段说明](#3.2.1 字段说明)
- [3.3 失败响应示例](#3.3 失败响应示例)
- 三、注册实现-控制层+服务层代码
-
- 1、返回结果实体类
- 2、注册参数实体类
-
- [2.1 序列化解释](#2.1 序列化解释)
- 3、控制层
- 4、服务层
-
- [4.1 服务层实体类DTO](#4.1 服务层实体类DTO)
- 5、控制层
- 6、注册参数校验
- 7、注册服务层实现-校验基本参数
-
- [7.1 完善服务层异常](#7.1 完善服务层异常)
- 8、注册实现-校验邮箱
- 四、附录:补充枚举类的使用
-
- 1、先搞懂:枚举到底是什么?(概念)
- 2、结合完整代码,逐字逐句讲语法
-
- [2.1 `public enum UserIdentityEnum`](#2.1
public enum UserIdentityEnum) - [2.2 最核心:枚举常量(整个枚举最重要的部分)](#2.2 最核心:枚举常量(整个枚举最重要的部分))
- [2.3 `private String message;`](#2.3
private String message;) - [2.4 `@Getter` 注解](#2.4
@Getter注解) - [2.5 构造方法](#2.5 构造方法)
- [2.1 `public enum UserIdentityEnum`](#2.1
- 3、枚举底层本质
- 4、最基础语法全部总结
- 5、到底怎么用?
- 6、把所有概念浓缩成你能一辈子记住的大白话
- 7、解决所有疑惑盲区
一、业务逻辑
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、先搞懂:枚举到底是什么?(概念)
生活例子
你现在做的用户身份,只有两种:
- 管理员
- 普通用户
以后永远不会多出第三种,也不会少掉,固定死的几个选项。
这种固定、有限、不会随便新增 的选项,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:代码里用的代号(英文大写,程序员内部用)("管理员"):这个代号对应的中文含义,给人看的- 多个选项之间用 逗号 , 隔开
- 所有选项写完,最后加一个 分号 ; 结束
语法死规则:
- 所有枚举常量,必须写在整个类的最最最上面
- 名字全部大写,下划线分隔(行业规范)
- 逗号分隔,最后一个结尾必须分号
你可以把它理解成:
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 对象(参数),给对象属性赋值。
枚举的构造方法非常特殊!
- 你永远不能手动 new 枚举
你写不了new UserIdentityEnum("管理员")这种代码,编译器直接报错。 - 这个构造方法,只有枚举自己内部能用
就是专门给上面的ADMIN("管理员")、NORMAL("普通用户")用的!
执行过程你一看就懂:
- 读到
ADMIN("管理员") - 自动调用这个构造方法
- 把
"管理员"传给形参message 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、把所有概念浓缩成你能一辈子记住的大白话
- 枚举是什么?
固定不变、数量有限的选项集合,用来替代硬编码字符串。 - ADMIN、NORMAL 是什么?
就是一个个固定好的对象,程序里全局唯一。 - 括号里的中文干嘛的?
附带的描述信息。 - message 变量干嘛的?
存放这个中文。 - 构造方法干嘛的?
创建对象时,自动把中文存进 message,你不用手动调用。 - @Getter 干嘛的?
对外提供 get 方法,让你能取出里面的中文。 - 使用规则
取值用枚举.常量.getMessage()
判断相等直接用 ==,不用 equals!
7、解决所有疑惑盲区
- 为什么构造方法不写 public?
枚举构造方法天生私有,禁止外部 new 对象,保证只能用内部固定的那几个。 - 为什么常量必须写最上面?
语法强制规定,枚举常量必须放在类体第一行。 - 为什么要用枚举,不用字符串?
- 不会写错字
- 代码有提示,IDEA 自动补全
- 类型安全,不会乱传奇怪字符串
- 统一管理所有选项,好找好改
- 为什么要用 message 存中文,不直接用英文?
代码里用英文ADMIN,数据库存英文;返回前端、展示页面、打印日志用中文。
最后,如果这篇文章对你有用,那么点赞收藏、评论区逛一逛我们下一篇继续实现用户注册功能👋~
