枚举是什么?
枚举是一种特殊的数据类型,预先定义一组常量(对象),并且必须为其赋值。
Java 枚举类型的基本想法非常简单:
这些类通过共有的静态final域为每个枚举常量导出一个实例。枚举类型没有可以访问的构造器,所以它是真的final类。客户端不能创建枚举类型的实例,也不能对它进行扩展,因此不存实例,而只存在声明过程的枚举常量。也就是枚举类型是实例受控的。它们是单例(Singleton)的范型化,本质上是单元素的枚举。
枚举使代码更易拓展和不一定代码减少,对比见下:
我们使用1234用来表示一年中的四个季节
1、常规代码
public String getSeasonString(Integer key) {
switch (key) {
case 1:
return "SPRING";
case 2:
return "SUMMER";
case 3:
return "AUTUMN";
case 4:
return "WINTER";
default:
return null;
}
}
2、使用枚举
// 定义枚举类
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum SeasonTwoArgs {
/**
* 春天
*/
SPRING(1, "春天"),
SUMMER(2, "夏天"),
AUTUMN(3, "秋天"),
WINTER(4, "冬天");
Integer key;
String msg;
SeasonTwoArgs(int key, String season) {
this.key = key;
this.msg = season;
// System.out.println("初始化:" + this.name() + "," + this.msg + "," + season);
}
// 很多情况,我们可能从前端拿到的值是枚举类的 key ,然后就可以通过以下静态方法获取到对应枚举值
public static SeasonTwoArgs valueofKey(Integer key) {
for (SeasonTwoArgs season : SeasonTwoArgs.values()) {
if (season.key == key) {
return season;
}
}
throw new IllegalArgumentException("No element matches " + key);
}
public String getMsg() {
return msg;
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public void setMsg(String msg) {
this.msg = msg;
}
// 获取 季节的方法
public String getSeasonString(Integer key) {
return SeasonTwoArgs.valueofKey(key).msg;
}
有人说了,在使用的时候,我都是调工具类里面的getSeasonString(String key)方法啊!
对比看来,发现代码并没有减少,反而增加了。
但是,如果后期发生了改变,使用枚举更加的方便,也不会影响到之前的业务逻辑。
枚举的要求:
-
它们限制了列名变量可以采取的值。
-
它们迫使您考虑列名可以采取的所有可能的值。
-
它们是一个常数,而不是一个数字,增加了源代码的可读性
自定义多参数枚举
代码见上。
枚举类型保证了编译时的类型安全。包含同名常量的多个枚举类型可以在一个系统中和平共处。因为每个类型都有自己的命名空间。可以新增或者重新排列枚举类型中的常量,而无需重新编译它的客户端代码。
枚举 使用 == 的安全性
1、代码编译时期:
如果俩个枚举类型不同,编译就会报错。
if (Season.SPRING.equals(TestSeason.AUTUMN)) ; // 编译正常 if (Season.SPRING == TestSeason.AUTUMN) ; // 编译失败,类型不匹配
2、运行时期:
Season season = null;
System.out.println(season == Season.SPRING);//正常运行
System.out.println(Season.SPRING.equals(season));//正常运行
System.out.println(season.equals(Season.SPRING));//空指针异常
switch中使用枚举
将枚举作为switch的参数
switch (season) {
case SPRING: {
System.out.println(1);
return 1;
}
case SUMMER: {
System.out.println(2);
return 2;
}
case AUTUMN: {
System.out.println(3);
return 3;
}
case WINTER: {
System.out.println(4);
return 4;
}
default:
System.out.println(0);
return 0;
}
如何输出JSON
1、自定义toJSONString方法
public String gtoJSONString() {
JSONObject object = new JSONObject();
object.put("name", this.getName());
return object.toJSONString();
}
2、借助jackson工具
1、类上面增加注解:
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Season {
2、get方法或者基本属性增加注解:
@JsonProperty("name")
public String getName() {
return this.name();
}
重写方法
/**
* 枚举计算类
*/
public enum Operation {
PLUS("+") {
@Override
public double apply(double x, double y) {
return x + y;
}
}, MINUS("-") {
@Override
public double apply(double x, double y) {
return x - y;
}
}, TIMES("*") {
@Override
public double apply(double x, double y) {
return x * y;
}
}, DIVIDE("/") {
@Override
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
public abstract double apply(double x, double y);
@Override
public String toString() {
return symbol;
}
public static void main(String[] args) {
double x = 4;
double y = 2;
for (Operation operation : Operation.values()) {
System.out.printf("%f %s %f = %f%n",
x, operation, y, operation.apply(x, y));
}
}
}
通过接口扩展枚举
虽然枚举类型是不可扩展的,但是接口类型确实可扩展的,它是用来表示API中的操作的接口类型。你可以定义另一个枚举类型,它实现这个接口,并用这个新类型的实例代替基本类型。
定义接口
public interface IOperation {
double apply(double x, double y);
}
枚举实现:
/**
* 枚举计算类
*/
public enum Operation implements IOperation{
PLUS("+") {
@Override
public double apply(double x, double y) {
return x + y;
}
}, MINUS("-") {
@Override
public double apply(double x, double y) {
return x - y;
}
}, TIMES("*") {
@Override
public double apply(double x, double y) {
return x * y;
}
}, DIVIDE("/") {
@Override
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
public static void main(String[] args) {
double x = 4;
double y = 2;
for (Operation operation : Operation.values()) {
System.out.printf("%f %s %f = %f%n",
x, operation, y, operation.apply(x, y));
}
}
}
扩展实现乘积运算:
public enum ExtOperation implements IOperation {
EXP("^") {
@Override
public double apply(double x, double y) {
return Math.pow(x, y);
}
};
private final String symbol;
ExtOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return this.symbol;
}
//入参实现IOperation接口并且是枚举类型。这个可以将该限定去掉,只要实现IOperation接口即可。
private static <T extends Enum<T> & IOperation> void test(Class<T> tClass, double x,
double y) {
for (IOperation operation : tClass.getEnumConstants()) {
System.out.printf("%f %s %f = %f%n",
x, operation, y, operation.apply(x, y));
}
}
public static void main(String[] args) {
test(ExtOperation.class,2,3);//使用扩展实现枚举对象
test(Operation.class,2,3);//使用默认的实现
}
}
可见:
枚举类型可以添加任意的方法和域,并实现任意的接口。它们提供了所有的Object方法的高级实现,实现了Comparable和Serializable接口,并针对枚举类型的可任意改变性提供了序列化方法。
EnumSet和EnumMap
1、EnumSet
EnumSet 是一种专门为枚举类型所设计的 Set 类型。
与HashSet相比,由于使用了内部位向量表示,因此它是特定 Enum 常量集的非常有效且紧凑的表示形式。
EnumSet 是抽象类,其有两个实现:RegularEnumSet 、JumboEnumSet,选择哪一个取决于实例化时枚举中常量的数量。但是 RegularEnumSet 和JumboEnumSet无法实例化,访问访问不是public
public void testEnumSet() {
// 使用场景 可用于 一批数据中,计算数据处于某种状态的有多少条/是否包含等
final EnumSet<Season> enumSet = EnumSet.of(Season.SPRING, Season.WINTER);
System.out.println("enumSet = " + enumSet);
enumSet.add(Season.SUMMER);
System.out.println("enumSet = " + enumSet);
System.out.println("enumSet.contains(Season.SPRING) = " + enumSet.contains(Season.SPRING));
}
2、EnumMap
EnumMap
是一个专门化的映射实现,用于将枚举常量用作键。与对应的 HashMap
相比,它是一个高效紧凑的实现,并且在内部表示为一个数组:
@Test
public void testEnumMap() {
//EnumMap是一个专门化的映射实现,用于将枚举常量用作键。
final EnumMap<Season, List<Person>> enumMap = new EnumMap<>(Season.class);
// 写一个投票的场景, 模拟10个人 回答自己喜欢的季节
for (int i = 0; i < 10; i++) {
List<Person> personList = null;
Season season = null;
if (i < 3) {
season = Season.SPRING;
} else if (i < 4) {
season = Season.SUMMER;
} else if (i < 5) {
season = Season.AUTUMN;
} else {
season = Season.WINTER;
}
if (enumMap.containsKey(season)) {
personList = enumMap.get(season);
} else {
personList = new ArrayList<>();
}
personList.add(new Person("name" + i, String.valueOf(i % 2), i + 18));
enumMap.put(season, personList);
}
enumMap.forEach((k, v) -> {
System.out.println("喜欢" + k + "有" + v.size() + "个");
});
// 输出:
/**
* 喜欢SPRING有3个
* 喜欢SUMMER有1个
* 喜欢AUTUMN有1个
* 喜欢WINTER有5个
*/
}
关于实现单例模式,天生就是单例模式!
关注公众号,点击关于我,加入QQ群,私聊群主可以联系到我哦,群文件有Java面试以及框架如何使用pdf等免费资源!!!
发哥讲Java
如果你觉得文章还不错,就● 扫码关注我们吧~
备注:如需搬迁或者转载,请备注原本链接