Java枚举的本质

目录

1.枚举简介

1.1.规范

1.2.枚举类真实的样子

1.3.枚举类的特点

1.4.枚举可以使用的方法

1.4.1.toString()方法

1.4.2.valueOf方法

1.4.3.values方法

1.4.4.ordinal方法

1.5.枚举的用法

1.5.1.常量

1.5.2.switch

1.5.3.枚举中增加方法

1.5.4.覆盖枚举方法

1.5.5.实现接口

1.5.5.1.情况 1:在 enum 类中实现接口

1.5.5.2.情况 2:让枚举类中的对象分别实现接口中的方法

1.5.6.在接口中使用枚举类

1.5.7.使用枚举集合

1.5.7.1.EnumSet

1.5.7.2.EnumMap

2.序列化和反序列化

2.1.什么是序列化和反序列化

2.2.什么是serialVersionUID序列化ID

2.3.什么类型的数据不能被序列化

3.jackson

3.1.jackson序列化writeValueAsString

3.2.jackson反序列化readValue

3.3.集合转换

3.4.@JsonProperty

3.5.ObjectMapper的一些配置

3.6.JsonParser

3.6.1.创建

3.6.2.解析

4.枚举的序列化

4.1.ordinal索引

4.2.时序图

4.3.json枚举序列化/反序列化处理

4.3.1.方法一:使用JsonCreator和JsonValue

4.3.2.方法二:自定义序列化/反序列化方法

4.4.mybatis枚举序列化/反序列化处理

4.4.1.TypeHandler

4.4.2.配置步骤

5.IDEA2019取消枚举提示

6.查看枚举调用的地方


1.枚举简介

枚举类型(enum type)是指由一组固定的常量组成合法的类型。
枚举类格式:

public enum SexEnum {MAN, WOMAN
}

1.1.规范

1.2.枚举类真实的样子

1、使用javac 命令编译,得到.class文件

2、使用命令 javap 对class文件进行反编译

>javac Sex.java

>javap Sex.class

得到如下结果:

Compiled from "Sex.java"

public final class Sex extends java.lang.Enum<Sex> {public static final Sex MAN;public static final Sex WOMAN;public static Sex[] values();public static Sex valueOf(java.lang.String);static {};
}

3、这里其实是创建了2个对象

public final class Sex extends java.lang.Enum<Sex> {public static final Sex MAN = new Sex();public static final Sex WOMAN = new Sex();public static Sex[] values();public static Sex valueOf(java.lang.String);static {};
}

1.3.枚举类的特点

1、枚举类是通过final修饰,不能被继承
2、枚举类默认继承了枚举类型 java.lang.Enum
3、枚举类的第一行罗列的是枚举类对象,并且是常量存储,所以枚举类的第一行写的是常量名称,默认存储了枚举对象。
4、枚举类的构造器是私有的。
5、枚举类相当于多例设计模式

1.4.枚举可以使用的方法

把上面的枚举类加上code和name

public enum SexEnum {MAN(1, "男"),WOMAN(2, "女");private Integer code;private String name;SexEnum(Integer code, String name) {this.code = code;this.name = name;}public Integer getCode() {return code;}public String getName() {return name;}
}

1.4.1.toString()方法

这个方法会返回枚举常量名

// MAN
System.out.println(SexEnum.MAN.toString());
// WOMAN
System.out.println(SexEnum.WOMAN.toString());

1.4.2.valueOf方法

这个方法用于构建枚举类,传入枚举类常量名即可。

SexEnum sexEnum = SexEnum.valueOf("MAN");
// 男
System.out.println(sexEnum.getName());
try {// 报错: java.lang.IllegalArgumentException: No enum constant com.leelen.scd.api.amc.enums.SexEnum.UNKNOWNSystem.out.println(SexEnum.valueOf("UNKNOWN").getName());
} catch (IllegalArgumentException e) {e.printStackTrace();
}

如果不存在传入的枚举常量,那么会报错:
java.lang.IllegalArgumentException: 
No enum constant com.leelen.scd.api.amc.enums.SexEnum.UNKNOWN
    at java.lang.Enum.valueOf(Enum.java:238)
    at com.leelen.scd.api.amc.enums.SexEnum.valueOf(SexEnum.java:3)
    at com.leelen.scd.api.amc.enums.UseSex.main(UseSex.java:28)

1.4.3.values方法

使用枚举类名进行调用,会返回所有枚举常量的数组

1.4.4.ordinal方法

这个方法会返回枚举常量在enum中声明的位置,从0开始

SexEnum[] sexEnum = SexEnum.values();
for (SexEnum s : sexEnum) {System.out.println(s.getCode() + "," + s.getName() + "," + s.ordinal());
}

打印结果:
1,男,0
2,女,1

1.5.枚举的用法

1.5.1.常量

public enum ColorEnum {RED, GREEN, BLANK, YELLOW
}

1.5.2.switch

public static void getSexName(SexEnum sexEnum) {switch (sexEnum) {case MAN:System.out.println("男");break;case WOMAN:System.out.println("女");break;default:break;}
}

这里不要使用 SexEnum.MAN, 不然会提示:An enum switch case label must be the unqualified name of an enumeration constant

1.5.3.枚举中增加方法

public class EnumTest {public static void main(String[] args) {ErrorCodeEnum errorCode = ErrorCodeEnum.SUCCESS;System.out.println("状态码:" + errorCode.code() +" 状态信息:" + errorCode.msg());}
}enum ErrorCodeEnum {SUCCESS(1000, "success"),PARAM_ERROR(1001, "parameter error"),SYS_ERROR(1003, "system error"),NAMESPACE_NOT_FOUND(2001, "namespace not found"),NODE_NOT_EXIST(3002, "node not exist"),NODE_ALREADY_EXIST(3003, "node already exist"),UNKNOWN_ERROR(9999, "unknown error");private int code;private String msg;ErrorCodeEnum(int code, String msg) {this.code = code;this.msg = msg;}public int code() {return code;}public String msg() {return msg;}public static ErrorCodeEnum getErrorCode(int code) {for (ErrorCodeEnum it : ErrorCodeEnum.values()) {if (it.code() == code) {return it;}}return UNKNOWN_ERROR;}
}

1.5.4.覆盖枚举方法

我们可以覆盖一些枚举中的方法用于实现自己的业务,比如我们可以覆盖 toString() 方法,实现代码如下:

public class EnumTest {public static void main(String[] args) {ColorEnum colorEnum = ColorEnum.RED;System.out.println(colorEnum.toString());}
}enum ColorEnum {RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4);//  成员变量private String name;private int index;//  构造方法private ColorEnum(String name, int index) {this.name = name;this.index = index;}//覆盖方法@Overridepublic String toString() {return this.index + ":" + this.name;}
}

1.5.5.实现接口

枚举类可以用来实现接口,但不能用于继承类,因为枚举默认继承了 java.lang.Enum 类,在 Java 语言中允许实现多接口,但不能继承多个父类,实现代码如下:

1.5.5.1.情况 1:在 enum 类中实现接口
public class EnumTest {public static void main(String[] args) {ColorEnum colorEnum = ColorEnum.RED;colorEnum.print();System.out.println("颜色:" + colorEnum.getInfo());}
}interface Behaviour {void print();String getInfo();
}enum ColorEnum implements Behaviour {RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4);private String name;private int index;private ColorEnum(String name, int index) {this.name = name;this.index = index;}@Overridepublic void print() {System.out.println(this.index + ":" + this.name);}@Overridepublic String getInfo() {return this.name;}
}
1.5.5.2.情况 2:让枚举类中的对象分别实现接口中的方法
public enum ColorEnum implements Behaviour{RED("红色", 1) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}}, GREEN("绿色", 2) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}}, BLANK("白色", 3) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}}, YELLOW("黄色", 4) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}};private String name;private int index;private ColorEnum(String name, int index) {this.name = name;this.index = index;}
}

1.5.6.在接口中使用枚举类

我们可以在一个接口中创建多个枚举类,用它可以很好的实现“多态”,也就是说我们可以将拥有相同特性,但又有细微实现差别的枚举类聚集在一个接口中,实现代码如下:

public class EnumTest {public static void main(String[] args) {// 赋值第一个枚举类ColorInterface colorEnum = ColorInterface.ColorEnum.RED;System.out.println(colorEnum);// 赋值第二个枚举类colorEnum = ColorInterface.NewColorEnum.NEW_RED;System.out.println(colorEnum);}
}interface ColorInterface {enum ColorEnum implements ColorInterface {GREEN, YELLOW, RED}enum NewColorEnum implements ColorInterface {NEW_GREEN, NEW_YELLOW, NEW_RED}
}

1.5.7.使用枚举集合

在 Java 语言中和枚举类相关的,还有两个枚举集合类 java.util.EnumSet 和 java.util.EnumMap,使用它们可以实现更多的功能。

1.5.7.1.EnumSet

使用 EnumSet 可以保证元素不重复,并且能获取指定范围内的元素,示例代码如下:

public class EnumTest {public static void main(String[] args) {List<ColorEnum> list = new ArrayList<>();list.add(ColorEnum.RED);list.add(ColorEnum.RED);  // 重复元素list.add(ColorEnum.YELLOW);list.add(ColorEnum.GREEN);// 去掉重复数据EnumSet<ColorEnum> enumSet = EnumSet.copyOf(list);System.out.println("去重:" + enumSet);// 获取指定范围的枚举(获取所有的失败状态)EnumSet<ErrorCodeEnum> errorCodeEnums = EnumSet.range(ErrorCodeEnum.ERROR, ErrorCodeEnum.UNKNOWN_ERROR);System.out.println("所有失败状态:" + errorCodeEnums);}
}enum ColorEnum {RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4);private String name;private int index;private ColorEnum(String name, int index) {this.name = name;this.index = index;}
}enum ErrorCodeEnum {SUCCESS(1000, "success"),ERROR(2001, "parameter error"),SYS_ERROR(2002, "system error"),NAMESPACE_NOT_FOUND(2003, "namespace not found"),NODE_NOT_EXIST(3002, "node not exist"),NODE_ALREADY_EXIST(3003, "node already exist"),UNKNOWN_ERROR(9999, "unknown error");private int code;private String msg;ErrorCodeEnum(int code, String msg) {this.code = code;this.msg = msg;}public int code() {return code;}public String msg() {return msg;}
}
1.5.7.2.EnumMap
public class EnumTest {public static void main(String[] args) {EnumMap<ColorEnum, String> enumMap = new EnumMap<>(ColorEnum.class);enumMap.put(ColorEnum.RED, "红色");enumMap.put(ColorEnum.GREEN, "绿色");enumMap.put(ColorEnum.BLANK, "白色");enumMap.put(ColorEnum.YELLOW, "黄色");System.out.println(ColorEnum.RED + ":" + enumMap.get(ColorEnum.RED));}
}enum ColorEnum {RED, GREEN, BLANK, YELLOW;
}

2.序列化和反序列化

2.1.什么是序列化和反序列化

序列化过程:是指把一个 Java 对象变成二进制内容,实质上就是一个 byte[]。因为序列化后可以把 byte[] 保存到文件中,或者把 byte[] 通过网络传输到远程(IO),如此就相当于把 Java 对象存储到文件或者通过网络传输出去了。
一个 Java 对象要能序列化,必须实现一个特殊的java.io.Serializable接口,它的定义如下:

package java.io;
public interface Serializable {
}

Serializable 没有定义任何方法,它是一个空接口。这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。

反序列化过程:把一个二进制内容(也就是 byte[])变回 Java 对象。有了反序列化,保存到文件中的 byte[] 又可以“变回” Java 对象,或者从网络上读取 byte[] 并把它“变回” Java 对象。

为什么需要序列化与反序列化?

当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。

当两个 Java 进程进行通信时,需要 Java 序列化与反序列化实现进程间的对象传送。换句话说,一方面,发送方需要把这个 Java 对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出 Java 对象。

代码:

import lombok.Data;
import java.io.Serializable;
@Data
public class Student implements Serializable {private String name;private Integer age;private Integer score;
}
import java.io.*;
public class TypeDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {serialize();deserialize();}/*** 序列化*/public static void serialize() throws IOException {Student student = new Student();student.setName("new");student.setAge(18);student.setScore(100);ObjectOutputStream objectOutputStream =new ObjectOutputStream(new FileOutputStream(new File("student.txt")));objectOutputStream.writeObject(student);objectOutputStream.close();System.out.println("序列化成功!已经生成student.txt文件");System.out.println("============================");}/*** 反序列化*/public static void deserialize() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream(new File("student.txt")));Student student = (Student) objectInputStream.readObject();objectInputStream.close();System.out.println("反序列化结果为:" + student);}
}

结果:

序列化成功!已经生成student.txt文件

============================

反序列化结果为:Student(name=new, age=18, score=100)

2.2.什么是serialVersionUID序列化ID

private static final long serialVersionUID = -4392658638228508589L;

serialVersionUID 是一个常数,用于唯一标识可序列化类的版本。

从输入流构造对象时,JVM 在反序列化过程中检查此常数。如果正在读取的对象的 serialVersionUID 与类中指定的序列号不同,则 JVM 抛出InvalidClassException。这是为了确保正在构造的对象与具有相同 serialVersionUID 的类兼容。

我们先调用serialize()方法把上面的Student对象序列化进student.txt文件中。然后修改Student类的内容去掉其中一个字段:

@Data
public class Student implements Serializable {private String name;private Integer age;
}

再调用反序列化方法deserialize(),结果报错:

Exception in thread "main" java.io.InvalidClassException: com.codejam.enums.demo.type.Student; local class incompatible: stream classdesc serialVersionUID = -6951954515964250676, local class serialVersionUID = 7327139321132172307

这是因为serialVersionUID 是可选的。如果不显式声明,Java 编译器将自动生成一个。

2.3.什么类型的数据不能被序列化

声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。

3.jackson

由于Spring自带的序列化和反序列化使用的是jackson,所以项目不使用fastjson。

3.1.jackson序列化writeValueAsString

先创建对象

@Data
public class Student {private String name;private Integer age;
}

创建保护集合的对象

@Data
public class Room {List<Student> studentList;
}

1、使用writeValueAsString将一个对象序列化为字符串

public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper=new ObjectMapper();Student student=new Student();student.setName("abc");student.setAge(18);String s = objectMapper.writeValueAsString(student);System.out.println(s);
}

打印:

{"name":"abc","age":18}、

2、使用writeValueAsString 将一个包含集合的对象序列化为字符串:

public static void main(String[] args) throws JsonProcessingException {List<Student> list = new ArrayList<>();for (int i = 0; i < 3; i++) {Student student = new Student();student.setName("name" + i);student.setAge(i);list.add(student);}Room classRoom = new Room();classRoom.setStudentList(list);ObjectMapper objectMapper = new ObjectMapper();String s = objectMapper.writeValueAsString(classRoom);System.out.println(s);
}

打印:

{"studentList":[{"name":"name0","age":0},{"name":"name1","age":1},{"name":"name2","age":2}]}

3.2.jackson反序列化readValue

使用readValue将字符串转换为student对象

public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper=new ObjectMapper();String s = "{\"name\":\"abc\",\"age\":18}";Student student = objectMapper.readValue(s, Student.class);System.out.println(student);
}

使用readValue将字符串转为对象(包含集合)

public static void main(String[] args) throws JsonProcessingException {String s = "{\"studentList\":[{\"name\":\"name0\",\"age\":0},{\"name\":\"name1\",\"age\":1},{\"name\":\"name2\",\"age\":2}]}";ObjectMapper objectMapper = new ObjectMapper();Room room = objectMapper.readValue(s, Room.class);System.out.println(room);
}

3.3.集合转换

public static void main(String[] args) throws JsonProcessingException {String listStr = "[{\"name\":\"李四\",\"age\":1},{\"name\":\"张三\",\"age\":2}]";ObjectMapper objectMapper = new ObjectMapper();List<Student> students = objectMapper.readValue(listStr, new TypeReference<List<Student>>() {});System.out.println(students);
}

3.4.@JsonProperty

如果序列化的时候,名称要更改的话,则可以使用@JsonProperty

3.5.ObjectMapper的一些配置

通过以下设置会在序列化和反序列化时忽略无法解析和为空的字段

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

3.6.JsonParser

JsonParser 类是底层 Json解析器。
JsonParser实现相较于 ObjectMapper 更底层,因此解析速度更快,但相对复杂。

3.6.1.创建

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;public static void main(String[] args) throws IOException {String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();JsonParser parser = factory.createParser(carJson);
}

createParser()方法传入 ReaderInputStreamURLbyte[] 或 char[] 参数可以实现解析不同来源 json 数据。

3.6.2.解析

JsonParser 工作方式是将 json 分解成一系列标记 (token) ,逐个迭代这些标记进行解析

public static void main(String[] args) throws IOException {String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();JsonParser parser  = factory.createParser(carJson);while(!parser.isClosed()){JsonToken jsonToken = parser.nextToken();System.out.println("jsonToken = " + jsonToken);}
}

输出结果:

jsonToken = START_OBJECT

jsonToken = FIELD_NAME

jsonToken = VALUE_STRING

jsonToken = FIELD_NAME

jsonToken = VALUE_NUMBER_INT

jsonToken = END_OBJECT

jsonToken = null

通过 JsonParser 的 nextToken() 方法获得 JsonToken,我们可以检查 JsonToken 实例的类型,JsonToken 类提供了一组常量表示标记类型:

package com.fasterxml.jackson.core;
public enum JsonToken
{NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),START_OBJECT("{", JsonTokenId.ID_START_OBJECT),END_OBJECT("}", JsonTokenId.ID_END_OBJECT),START_ARRAY("[", JsonTokenId.ID_START_ARRAY),END_ARRAY("]", JsonTokenId.ID_END_ARRAY),FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT),VALUE_STRING(null, JsonTokenId.ID_STRING),VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),VALUE_TRUE("true", JsonTokenId.ID_TRUE),VALUE_FALSE("false", JsonTokenId.ID_FALSE),VALUE_NULL("null", JsonTokenId.ID_NULL),;
}

如果标记指针指向的是字段,JsonParser 的 getCurrentName() 方法返回当前字段名称。

getValueAsString() 返回当前标记值的字符串类型,同理 getValueAsInt() 返回整型值
其他方法

public static void main(String[] args) throws IOException {String json = "{ \"name\" : \"tom\", \"age\" : 28, \"height\": 1.75, \"ok\": true}";JsonFactory factory = new JsonFactory();JsonParser parser = factory.createParser(json);while (!parser.isClosed()) {JsonToken token = parser.nextToken();if (JsonToken.FIELD_NAME == token) {String fieldName = parser.getCurrentName();System.out.print(fieldName + ": ");parser.nextToken();switch (fieldName) {case "name":System.out.println(parser.getValueAsString());break;case "age":System.out.println(parser.getValueAsInt());break;case "height":System.out.println(parser.getValueAsDouble());break;case "ok":System.out.println(parser.getValueAsBoolean());break;}}}
}

4.枚举的序列化

4.1.ordinal索引

枚举默认是使用索引ordinal 来进行序列化反序列化操作的,
例如我这边定义了枚举:1, 3  它对应的索引是0,1

@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum AmcManagementModeEnum implements IDictionaryEnum {UNIFIED_AUTHORIZATION_MANAGEMENT(1, "统一授权管理"),//SEMI_AUTHORIZED_MANAGEMENT(2, "半授权管理"),INDEPENDENT_MANAGEMENT(3, "独立管理"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;
}

前端请求0的时候,对应的是第一个 1, "统一授权管理"。
请求1的时候,对应的是第二个 3, "独立管理"。

但如果我请求2的话,则会报错越界了。

Invalid JSON input: Cannot deserialize value of type `com.leelen.scd.module.amc.enums.AmcManagementModeEnum` from number 2: index value outside legal index range [0..1]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.leelen.scd.module.amc.enums.AmcManagementModeEnum` from number 2: index value outside legal index range [0..1]\n at [Source: (PushbackInputStream); line: 17, column: 31] (through reference chain: com.leelen.scd.base.common.entity.RequestDTO[\"body\"]->com.leelen.scd.base.common.entity.PagerReqDTO[\"params\"]->com.leelen.scd.module.amc.vo.AmcNeighInfoPageReq[\"managementMode\"])

4.2.时序图

4.3.json枚举序列化/反序列化处理

4.3.1.方法一:使用JsonCreator和JsonValue

JsonCreator :标记在反序列化时的初始化函数,入参为对应该枚举类型的值。

JsonVale:标记在序列化时枚举对应生成的值。

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.leelen.scd.base.common.enums.IDictEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.Accessors;import java.util.Objects;@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum AmcManagementModeEnum implements IDictEnum{UNIFIED_AUTHORIZATION_MANAGEMENT( 1, "统一授权管理"),//SEMI_AUTHORIZED_MANAGEMENT(2, "半授权管理"),INDEPENDENT_MANAGEMENT(3, "独立管理"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;/*** 处理入参,定义转换函数parse,将code值入参转成对应的枚举类型* 在反序列化的时候Jackson会自动调用这个方法去自动帮我们转换*/@JsonCreatorpublic static AmcManagementModeEnum parse(Integer code) {if (Objects.isNull(code)) {return null;}for (AmcManagementModeEnum item : AmcManagementModeEnum.values()) {if (item.code.equals(code)) {return item;}}return null;}/*** 处理出参,在getter方法标记序列化后的值*/@JsonValuepublic Integer code() {return code;}}

使用枚举:
请求参数:

import com.leelen.scd.module.amc.enums.AmcManagementModeEnum;
import lombok.Data;
@Data
public class AmcNeighInfoPageReq {/*** 小区管理模式*/private AmcManagementModeEnum managementMode;
}

响应参数:

@Data
public class AmcNeighInfoPageRes {/*** 小区管理模式*/private AmcManagementModeEnum managementMode;
}

验证:

@RestController
@RequestMapping("/web/system/community/amc")
public class AmcNeighInfoController {/*** 分页*/@RequestMapping("/page")public ResponseDTO<AmcNeighInfoPageRes> page(@RequestBody final RequestDTO<PagerReqDTO<AmcNeighInfoPageReq>> request) {AmcNeighInfoPageRes res = new AmcNeighInfoPageRes();res.setManagementMode(request.getBody().getParams().getManagementMode());return ResponseHelper.successResponse(request.getHeader(), res);}
}

4.3.2.方法二:自定义序列化/反序列化方法

1、首先定义接口IDictEnum。 
这里指定反序列化的方法:EnumJsonDeserializer
这里指定序列化的方法:EnumJsonSerializer

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;@JsonDeserialize(using = EnumJsonDeSerializer.class)
@JsonSerialize(using = EnumJsonSerializer.class)
public interface IDictEnum {/*** 获得字典码值*/Integer code();/*** 获得字典描述, 这里不能用name, 因为java.lang.Enum已经定义了name*/String desc();}

2、反序列化方法:EnumJsonDeserializer

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;import java.io.IOException;
import java.util.Arrays;public class EnumJsonDeSerializer extends JsonDeserializer<IDictEnum> implements ContextualDeserializer {private Class<? extends IDictEnum> clazz;public EnumJsonDeSerializer() {}public EnumJsonDeSerializer(Class<? extends IDictEnum> clazz) {this.clazz = clazz;}@Overridepublic IDictEnum deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {String param = jsonParser.getText();IDictEnum[] enumConstants = clazz.getEnumConstants();JsonStreamContext parsingContext = jsonParser.getParsingContext();IDictEnum iDictEnum = Arrays.stream(enumConstants).filter(x -> {//x.toString(),取枚举的具体值,如:xxx.enums.share.DelFlagEnum 枚举里的“NOT_DELETE”//从而使得两种形式都能识别String enumCodeStr = x.toString();return enumCodeStr.equals(param) || param.equals(x.code() + "");}).findFirst().orElse(null);/*if (null == iEnum) {String msg = String.format("枚举类型%s从%s未能转换成功", clazz.toString(), param);throw new Exception(msg);}*/return iDictEnum;}@Overridepublic Class<?> handledType() {return IDictEnum.class;}@SuppressWarnings({"unchecked"})@Overridepublic JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)throws JsonMappingException {JavaType type = property.getType();// 如果是容器,则返回容器内部枚举类型while (type.isContainerType()) {type = type.getContentType();}return new EnumJsonDeSerializer((Class<? extends IDictEnum>) type.getRawClass());}
}

3、序列化方法:EnumJsonSerializer

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class EnumJsonSerializer extends JsonSerializer<IDictEnum> {public void serialize(IDictEnum iDictEnum, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {// 序列化只要code的值generator.writeNumber(iDictEnum.code());// 序列化形式: {"code": "", "desc": ""}//generator.writeStartObject();//generator.writeNumberField("code", iBaseDict.code());//generator.writeStringField("desc", iBaseDict.desc());//generator.writeEndObject();}
}

经验证以下这几种情况都能很好的识别成功!!!

@Data
public class AmcNeighInfoPageReq {private AmcManagementModeDictEnum managementMode;private List<AmcManagementModeDictEnum> managementModeList;private AmcNeighInfoPageReq amcNeighInfoPageReq;private Map<Integer, AmcManagementModeDictEnum> map1;private Map<AmcManagementModeDictEnum, Integer> map2;
}

4.4.mybatis枚举序列化/反序列化处理

以上两种方法对于mybatis来说没有起到作用,需要单独进行mybatis自定义序列化

MyBatis内置了两个枚举转换器分别是:org.apache.ibatis.type.EnumTypeHandler和org.apache.ibatis.type.EnumOrdinalTypeHandler。

  • EnumTypeHandler是默认的枚举转换器,该转换器将枚举实例转换为实例名称的字符串。比如有个枚举。

例如 前端输入1的话,则后端insert的时候的值是

字符串“UNIFIED_AUTHORIZATION_MANAGEMENT”

@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum AmcManagementModeEnum implements IDictEnum {UNIFIED_AUTHORIZATION_MANAGEMENT( 1, "统一授权管理"),//SEMI_AUTHORIZED_MANAGEMENT(2, "半授权管理"),INDEPENDENT_MANAGEMENT(3, "独立管理"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;
}
  • EnumOrdinalTypeHandler这个转换器将枚举实例的ordinal属性作为取值(从0依次取值)。还是上面的例子用这种转换器保存在数据库中的值就是0。

如果我们想保存枚举本身所定义的code值呢?这就需要自定义一个类型转换器,自定义一个int类型保存在数据库,即insert时枚举转换为int型数据保存在数据库,select时数据库中的int值转换成实体类的枚举类型。

4.4.1.TypeHandler

TypeHandler,顾名思义类型转换器,就是将数据库中的类型与Java中的类型进行相互转换的处理器。

经常自定义类型转换器方式有两种,实现 TypeHandler 接口, 或继承抽象类 BaseTypeHandle,并且可以指定转换后的字段类型。

  • 其实BaseTypeHandler也是继承了TypeHandler接口,在实现的TypeHandler接口的方法中调用的是自身抽象方法

抽象类BaseTypeHandler的抽象方法

// 执行之前,将Java类型转换为对应的jdbc类型,用于赋值sql中参数
public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
// 根据列名从resultSet中获取,将JDBC类型转换为Java类型
public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;
// 根据下标从resultSet中获取,将JDBC类型转换为Java类型
public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;
// 用于在执行完存储过程后,将JDBC类型转换为Java类型
public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;

4.4.2.配置步骤

1、mybatis自定义枚举类型转换 (这里需要配置@MappedTypes)

import com.leelen.scd.base.common.util.EnumUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;@MappedTypes({IDictEnum.class})
public class EnumTypeHandler<E extends Enum<?> & IDictEnum> extends BaseTypeHandler<IDictEnum> {private Class<E> type;public EnumTypeHandler(Class<E> type) {if (type == null) {throw new IllegalArgumentException("Type argument cannot be null.");}this.type = type;}/*** 用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型*/@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, IDictEnum parameter, JdbcType jdbcType)throws SQLException {ps.setInt(i, parameter.code());}/*** 用于定义通过字段名称获取字段数据时,如何把数据库类型转换为对应的Java类型*/@Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {int code = rs.getInt(columnName);return rs.wasNull() ? null : codeOf(code);}/*** 用于定义通过字段索引获取字段数据时,如何把数据库类型转换为对应的Java类型*/@Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {int code = rs.getInt(columnIndex);return rs.wasNull() ? null : codeOf(code);}/*** 用定义调用存储过程后,如何把数据库类型转换为对应的Java类型*/@Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {int code = cs.getInt(columnIndex);return cs.wasNull() ? null : codeOf(code);}private E codeOf(int code) {try {return EnumUtil.getEnumByCode(type, code);} catch (Exception ex) {throw new IllegalArgumentException("Cannot convert " + code + " to " + type.getSimpleName() + " by code value.", ex);}}
}

这里用到了工具类:

public class EnumUtil {/*** 根据code获取枚举*/public static <T extends IDictEnum> T getEnumByCode(Class<T> tClass, Integer code) {if (code != null) {for (T t : tClass.getEnumConstants()) {if (t.code().equals(code)) {return t;}}}return null;}
}

2、定义mybatis的typeHandler扫描包路径:

# mybatis配置参数
mybatis:# 定义typeHandler扫描包路径type-handlers-package: com.leelen.scd

5.IDEA2019取消枚举提示

IDEA2019枚举自带入参提示,个人感觉看的比较眼花,建议把他取消掉。

Settings - Editor - Inlay Hints - Java 选择Parameter hints取消勾选Enum constants

效果如下:

6.查看枚举调用的地方

我们通常使用 ctrl + 鼠标左键,来查看方法被哪些地方调用,但是枚举却没法这么使用。

解决方法,使用alt + F7 来间接查看调用的地方,或者右键,选择Find Usage

或者使用快捷键 ctl + alt + F7

也可以在idea配置提示:

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

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

相关文章

工具推荐:市面上有哪些带有ai问答机器人的SaaS软件

众所周知&#xff0c;SaaS&#xff08;软件即服务&#xff09;模式下的AI问答机器人已经逐渐成为企业、个人在办公、生活和学习中的辅助工具。ai问答机器人凭借高效、便捷、智能的特点&#xff0c;为用户提供了全新的交互体验。本文将推荐几款市面上好用的带有ai问答机器人的Sa…

【文心智能体】创建一个属于自己的生活情感类智能体

文章目录 前言一、创建智能体二、体验 前言 智能体技术的快速发展&#xff0c;进一步激发了各行业开发者对其实际应用及用户需求的深入探索。 创建一个属于自己的智能体。文心一言提供了一个很好的平台。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考…

GpuMall智算云:Ubuntu 实例桌面版

基于 ubuntu18.04 安装的桌面版本&#xff0c;桌面使用 xfce4 &#xff0c;集成了 Pytorch2.3.0、cuda11.8、Python3.10、VNC、noVNC、VSCode-Server。 在 镜像市场 选择xfce4-desktop镜像&#xff0c;然后进行创建实例 GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall…

一维前缀和[模版]

题目链接 题目: 分析: 因为要求数组中连续区间的和, 可以使用前缀和算法注意:下标是从1开始算起的, 真正下标0的位置是0第一步: 预处理出来一个前缀和数组dp dp[i] 表示: 表示[1,i] 区间所有元素的和dp[i] dp[i-1] arr[i]例如示例一中: dp数组为{1,3,7}第二步: 使用前缀数…

RabbitMQ - SimpleMessageListenerContainer的实现逻辑

RabbitMQ - SimpleMessageListenerContainer的实现逻辑 Queue&#xff08;队列&#xff09;&#xff1a;在 RabbitMQ 中用于存储消息的数据结构。生产者将消息发送到队列中&#xff0c;而消费者从队列中接收消息。 Connection&#xff08;连接&#xff09;&#xff1a;连接是应…

Python代码实现代价函数

最小二乘法 最小二乘法是一种在统计学、数学、工程学和计算机科学等领域广泛使用的优化方法。 基本原理 最小二乘法的主要目的是找到一组模型参数&#xff0c;使得根据这些参数所预测的数据与实际观测数据之间的差异&#xff08;即残差&#xff09;的平方和最小。 数学表达…

5.22R语言初步学习-1

今天上课讲R语言&#xff0c;要干什么没讲&#xff0c;分析什么&#xff0c;目的是什么没讲。助教基本上就是让我们打开窗口&#xff0c;按要求抄代码指令&#xff0c;代码原理也没讲......再加上最近正好在学概率论与数理统计&#xff0c;肯定是有用的&#xff0c;所以还是学习…

PHP质量工具系列之php_CodeSniffer

PHP_CodeSniffer 是一组两个 PHP 脚本&#xff1a;主脚本 phpcs 对 PHP、JavaScript 和 CSS 文件进行标记&#xff0c;以检测是否违反定义的编码标准&#xff1b;第二个脚本 phpcbf 自动纠正违反编码标准的行为。PHP_CodeSniffer 是一个重要的开发工具&#xff0c;可以确保你的…

【简单介绍下近邻算法】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Python-3.12.0文档解读-内置函数hash()详细说明+记忆策略+常用场景+巧妙用法+综合技巧

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 详细说明 功能描述 参数 返回值 特性 使用示例 注意事项 记忆策略 常用场景 …

Predictable MDP Abstraction for Unsupervised Model-Based RL

ICML 2023 paper code Intro 文章提出了一种用于无监督基于模型强化学的方法&#xff0c;称为可预测MDP抽象&#xff08;Predictable MDP Abstraction, PMA&#xff09;。在MBRL中&#xff0c;一个关键部分是能够准确建模环境动力学动态模型。然而&#xff0c;这个预测模型误…

【SQL国际标准】ISO/IEC 9075:2023 系列SQL的国际标准详情

目录 &#x1f30a;1. 前言 &#x1f30a;2. ISO/IEC 9075:2023 系列SQL的国际标准详情 &#x1f30a;1. 前言 ISO&#xff08;国际标准化组织&#xff0c;International Organization for Standardization&#xff09;是一个独立的、非政府间的国际组织&#xff0c;其宗旨是…

IPKISS ------ AMF 添加自定义层

IPKISS ------ AMF 添加自定义层 正文 正文 很多时候&#xff0c;我们想要添加属于我们自己的层&#xff0c;此时我们需要向 AMF pdk 中的 gdsii.py 和 layers.py 文件添加层的信息。这两个文件的目录如下&#xff1a; 在 gdsii.py 文件下的 TECH.GDSII.LAYERTABLE 字典中我们…

工业大模型带来智能生产新范式

在当前工业行业的发展背景下&#xff0c;大模型技术展现出广阔的应用前景&#xff0c;在提升专业知识的可获取性和传承、优化软件技术的应用、提高数据驱动决策的准确性和效率等方面拥有显著潜力。 ‍‍‍‍‍‍‍‍‍‍据了解&#xff0c;蓝卓“基于supOS工业操作系统的工业大…

查看目录或文件的磁盘使用情况

在排查问题过程中&#xff0c;会遇到磁盘占满&#xff0c;需要排查具体哪个文件占用比较大&#xff0c;此时可以使用du 命令 du [选项] [文件或目录...] 常用的选项包括&#xff1a; -h 或 --human-readable&#xff1a;以人类可读的格式&#xff08;如 K、M、G&#xff09;…

WAF绕过(下)

过流量检测 这里的流量检测就是在网络层的waf拦截到我们向webshell传输的数据包&#xff0c;以及webshell返回的数据 包&#xff0c;检测其中是否包含敏感信息的一种检测方式。如果是大马的情况下&#xff0c;可以在大马中添加多处判断代码&#xff0c;因此在执行大马提供的功…

吉林大学软件工程易错题

1.【单选题】软件工程方法是&#xff08; &#xff09;。 A、为开发软件提供技术上的解决方法 &#xff08;软件工程方法 &#xff09; B、为支持软件开发、维护、管理而研制的计算机程序系统&#xff08;软件工程工具&#xff09; …

Vue 3 的 setup语法糖工作原理

前言 我们每天写vue3项目的时候都会使用setup语法糖&#xff0c;但是你有没有思考过下面几个问题。setup语法糖经过编译后是什么样子的&#xff1f;为什么在setup顶层定义的变量可以在template中可以直接使用&#xff1f;为什么import一个组件后就可以直接使用&#xff0c;无需…

KDE-Ambari-Metrics-Collector问题排查解决手册

文档说明 本文档是为了解决KDE平台的Ambari-Metrics-Collector服务在运行时遇到的问题而提供的问题排查和解决方法的参考文档 说明: 当前的Ambari-Metrics-Collector服务包括了ams-collector和ams-hbase两个程序,在Ambari-Metrics-Collector安装的节点执行ps -elf|grep am…

远动通讯屏具体干啥作用

远动通讯屏具体干啥作用 远动通讯屏主要用于电力系统中的各类发电厂、变电站、光伏电站、开闭所、配电房等&#xff0c;具有实时传输数据和远程控制功能。它的主要作用包括&#xff1a; 数据采集&#xff1a;远动通讯屏能够采集各种模拟量、开关量和数字量等信息&#xff0c…