打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
掌握Java枚举这几个知识点,日常开发就够啦

前言

春节来临之际,祝大家新年快乐哈。整理了Java枚举的相关知识,算是比较基础的,希望大家一起学习进步。

一、枚举类型是什么?

JDK5引入了一种新特性,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这就是枚举类型。

一个枚举的简单例子

  1. enum SeasonEnum {

  2. SPRING,SUMMER,FALL,WINTER;

  3. }

二、 枚举类的常用方法

Enum常用方法有以下几种:

  • name(); 返回enum实例声明时的名字。

  • ordinal(); 返回一个int值,表示enum实例在声明的次序。

  • equals(); 返回布尔值,enum实例判断相等

  • compareTo() 比较enum实例与指定对象的顺序

  • values(); 返回enum实例的数组

  • valueOf(String name) 由名称获取枚举类中定义的常量

直接看例子吧:

  1. enum Shrubbery {

  2. GROUND,CRAWLING, HANGING

  3. }

  4. public class EnumClassTest {

  5. public static void main(String[] args) {

  6. //values 返回enum实例的数组

  7. for (Shrubbery temp : Shrubbery.values()) {

  8. // name 返回实例enum声明的名字

  9. System.out.println(temp.name() + " ordinal is " + temp.ordinal() + " ,equal result is " +

  10. Shrubbery.CRAWLING.equals(temp) + ",compare result is " + Shrubbery.CRAWLING.compareTo(temp));

  11. }

  12. //由名称获取枚举类中定义的常量值

  13. System.out.println(Shrubbery.valueOf("CRAWLING"));

  14. }

  15. }

运行结果:

  1. GROUND ordinal is 0 ,equal result is false,compare result is 1

  2. CRAWLING ordinal is 1 ,equal result is true,compare result is 0

  3. HANGING ordinal is 2 ,equal result is false,compare result is -1

  4. CRAWLING

三、枚举类的真面目

枚举类型到底是什么类呢?我们新建一个简单枚举例子,看看它的庐山真面目。如下:

  1. public enum Shrubbery {

  2. GROUND,CRAWLING, HANGING

  3. }

使用javac编译上面的枚举类,可得Shrubbery.class文件。

  1. javac Shrubbery.java

再用javap命令,反编译得到字节码文件。如:执行 javapShrubbery.class可到以下字节码文件。

  1. Compiled from "Shrubbery.java"

  2. public final class enumtest.Shrubbery extends java.lang.Enum<enumtest.Shrubbery> {

  3. public static final enumtest.Shrubbery GROUND;

  4. public static final enumtest.Shrubbery CRAWLING;

  5. public static final enumtest.Shrubbery HANGING;

  6. public static enumtest.Shrubbery[] values();

  7. public static enumtest.Shrubbery valueOf(java.lang.String);

  8. static {};

  9. }

从字节码文件可以发现:

  • Shrubbery枚举变成了一个final修饰的类,也就是说,它不能被继承啦。

  • Shrubbery是java.lang.Enum的子类。

  • Shrubbery定义的枚举值都是public static final修饰的,即都是静态常量。

为了看得更仔细,javap反编译加多个参数-c,执行如下命令:

  1. javap -c Shrubbery.class

静态代码块的字节码文件如下:

  1. static {};

  2. Code:

  3. 0: new #4 // class enumtest/Shrubbery

  4. 3: dup

  5. 4: ldc #7 // String GROUND

  6. 6: iconst_0

  7. 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V

  8. 10: putstatic #9 // Field GROUND:Lenumtest/Shrubbery;

  9. 13: new #4 // class enumtest/Shrubbery

  10. 16: dup

  11. 17: ldc #10 // String CRAWLING

  12. 19: iconst_1

  13. 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V

  14. 23: putstatic #11 // Field CRAWLING:Lenumtest/Shrubbery;

  15. 26: new #4 // class enumtest/Shrubbery

  16. 29: dup

  17. 30: ldc #12 // String HANGING

  18. 32: iconst_2

  19. 33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V

  20. 36: putstatic #13 // Field HANGING:Lenumtest/Shrubbery;

  21. 39: iconst_3

  22. 40: anewarray #4 // class enumtest/Shrubbery

  23. 43: dup

  24. 44: iconst_0

  25. 45: getstatic #9 // Field GROUND:Lenumtest/Shrubbery;

  26. 48: aastore

  27. 49: dup

  28. 50: iconst_1

  29. 51: getstatic #11 // Field CRAWLING:Lenumtest/Shrubbery;

  30. 54: aastore

  31. 55: dup

  32. 56: iconst_2

  33. 57: getstatic #13 // Field HANGING:Lenumtest/Shrubbery;

  34. 60: aastore

  35. 61: putstatic #1 // Field $VALUES:[Lenumtest/Shrubbery;

  36. 64: return

  37. }

  • 0-39行实例化了Shrubbery枚举类的GROUND,CRAWLING, HANGING;

  • 40-64为创建Shrubbery[]数组$VALUES,并将上面的三个实例化对象放入数组的操作。

  • 因此,枚举类方法values()返回enum枚举实例的数组是不是豁然开朗啦。

四、枚举类的优点

枚举类有什么优点呢?就是我们为什么要选择使用枚举类呢?因为它可以增强代码的可读性,可维护性,同时,它也具有安全性。

枚举类可以增强可读性、可维护性

假设现在有这样的业务场景:订单完成后,通知买家评论。很容易有以下代码:

  1. //订单已完成

  2. if(3==orderStatus){

  3. //do something

  4. }

很显然,这段代码出现了魔法数,如果你没写注释,谁知道3表示订单什么状态呢,不仅阅读起来比较困难,维护起来也很蛋疼?如果使用枚举类呢,如下:

  1. public enum OrderStatusEnum {

  2. UNPAID(0, "未付款"), PAID(1, "已付款"), SEND(2, "已发货"), FINISH(3, "已完成"),;


  3. private int index;


  4. private String desc;


  5. public int getIndex() {

  6. return index;

  7. }


  8. public String getDesc() {

  9. return desc;

  10. }


  11. OrderStatusEnum(int index, String desc) {

  12. this.index = index;

  13. this.desc = desc;

  14. }

  15. }


  16. //订单已完成

  17. if(OrderStatusEnum.FINISH.getIndex()==orderStatus){

  18. //do something

  19. }

可见,枚举类让这段代码可读性更强,也比较好维护,后面加个新的订单状态,直接添加多一种枚举状态就可以了。有些朋友认为, publicstaticfinalint这种静态常量也可以实现该功能呀,如下:

  1. public class OrderStatus {

  2. //未付款

  3. public static final int UNPAID = 0;

  4. public static final int PAID = 1;

  5. public static final int SENDED = 2;

  6. public static final int FINISH = 3;


  7. }


  8. //订单已完成

  9. if(OrderStatus.FINISH==orderStatus){

  10. //do something

  11. }

当然,静态常量这种方式实现,可读性是没有任何问题的,日常工作中代码这样写也无可厚非。但是,定义int值相同的变量,容易混淆,如你定义 PAIDSENDED状态都是2,编译器是不会报错的。

因此,枚举类第一个优点就是可读性,可维护性都不错,所以推荐。

枚举类安全性

除了可读性、可维护性外,枚举类还有个巨大的优点,就是安全性。

从上一节枚举类字节码分析,我们知道:

  • 一个枚举类是被final关键字修饰的,不能被继承。

  • 并且它的变量都是public static final修饰的,都是静态变量。

当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。

五、枚举的常见用法

enum组织常量

在JDK5之前,常量定义都是这样,先定义一个类或者接口,属性类型都是public static final...,有了枚举之后,可以把常量组织到枚举类了,如下:

  1. enum SeasonEnum {

  2. SPRING,SUMMER,FALL,WINTER,;

  3. }

enum与switch 环环相扣

一般来说,switch-case中只能使用整数值,但是枚举实例天生就具备整数值的次序,因此,在switch语句中是可以使用enum的,如下:

  1. enum OrderStatusEnum {

  2. UNPAID, PAID, SEND, FINISH

  3. }

  4. public class OrderStatusTest {

  5. public static void main(String[] args) {

  6. changeByOrderStatus(OrderStatusEnum.FINISH);

  7. }


  8. static void changeByOrderStatus(OrderStatusEnum orderStatusEnum) {

  9. switch (orderStatusEnum) {

  10. case UNPAID:

  11. System.out.println("老板,你下单了,赶紧付钱吧");

  12. break;

  13. case PAID:

  14. System.out.println("我已经付钱啦");

  15. break;

  16. case SENDED:

  17. System.out.println("已发货");

  18. break;

  19. case FINISH:

  20. System.out.println("订单完成啦");

  21. break;

  22. }

  23. }

  24. }

在日常开发中,enum与switch一起使用,会让你的代码可读性更好哦。

向枚举中添加新的方法

可以向枚举类添加新方法的,如get方法,普通方法等,以下是日常工作最常用的一种枚举写法:

  1. public enum OrderStatusEnum {

  2. UNPAID(0, "未付款"), PAID(1, "已付款"), SENDED(2, "已发货"), FINISH(3, "已完成"),;


  3. //成员变量

  4. private int index;

  5. private String desc;


  6. //get方法

  7. public int getIndex() {

  8. return index;

  9. }


  10. public String getDesc() {

  11. return desc;

  12. }


  13. //构造器方法

  14. OrderStatusEnum(int index, String desc) {

  15. this.index = index;

  16. this.desc = desc;

  17. }


  18. //普通方法

  19. public static OrderStatusEnum of(int index){

  20. for (OrderStatusEnum temp : values()) {

  21. if (temp.getIndex() == index) {

  22. return temp;

  23. }

  24. }

  25. return null;

  26. }

  27. }

枚举实现接口

所有枚举类都继承于java.lang.Enum,所以枚举不能再继承其他类了。但是枚举可以实现接口呀,这给枚举增添了不少色彩。如下:

  1. public interface ISeasonBehaviour {


  2. void showSeasonBeauty();


  3. String getSeasonName();

  4. }

  5. public enum SeasonEnum implements ISeasonBehaviour {

  6. SPRING(1,"春天"),SUMMER(2,"夏天"),FALL(3,"秋天"),WINTER(4,"冬天"),

  7. ;


  8. private int index;

  9. private String name;


  10. SeasonEnum(int index, String name) {

  11. this.index = index;

  12. this.name = name;

  13. }


  14. public int getIndex() {

  15. return index;

  16. }

  17. public String getName() {

  18. return name;

  19. }


  20. //接口方法

  21. @Override

  22. public void showSeasonBeauty() {

  23. System.out.println("welcome to " + this.name);

  24. }


  25. //接口方法

  26. @Override

  27. public String getSeasonName() {

  28. return this.name;

  29. }

  30. }

使用接口组织枚举

  1. public interface Food {

  2. enum Coffee implements Food{

  3. BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO

  4. }

  5. enum Dessert implements Food{

  6. FRUIT, CAKE, GELATO

  7. }

  8. }

六、枚举类比较是用==还是equals?

先看一个例子,如下:

  1. public class EnumTest {

  2. public static void main(String[] args) {


  3. Shrubbery s1 = Shrubbery.CRAWLING;

  4. Shrubbery s2 = Shrubbery.GROUND;

  5. Shrubbery s3 = Shrubbery.CRAWLING;


  6. System.out.println("s1==s2,result: " + (s1 == s2));

  7. System.out.println("s1==s3,result: " + (s1 == s3));

  8. System.out.println("Shrubbery.CRAWLING.equals(s1),result: "+Shrubbery.CRAWLING.equals(s1));

  9. System.out.println("Shrubbery.CRAWLING.equals(s2),result: "+Shrubbery.CRAWLING.equals(s2));


  10. }

  11. }

运行结果:

  1. s1==s2,result: false

  2. s1==s3,result: true

  3. Shrubbery.CRAWLING.equals(s1),result: true

  4. Shrubbery.CRAWLING.equals(s2),result: false

可以发现不管用==还是equals,都是可以的。其实枚举的equals方法,就是用==比较的,如下:

  1. public final boolean equals(Object other) {

  2. return this==other;

  3. }

七、枚举实现的单例

effective java提过,最佳的单例实现模式就是枚举模式。单例模式的实现有好几种方式,为什么是枚举实现的方式最佳呢?

因为枚举实现的单例有以下优点:

  • 枚举单例写法简单

  • 枚举可解决线程安全问题

  • 枚举可解决反序列化会破坏单例的问题

一个枚举单例demo如下:

  1. public class SingletonEnumTest {

  2. public enum SingletonEnum {

  3. INSTANCE,;

  4. private String name;


  5. public String getName() {

  6. return name;

  7. }


  8. public void setName(String name) {

  9. this.name = name;

  10. }

  11. }


  12. public static void main(String[] args) {

  13. SingletonEnum.INSTANCE.setName("jay@huaxiao");

  14. System.out.println(SingletonEnum.INSTANCE.getName());

  15. }

  16. }

有关于枚举实现单例,想深入了解的朋友可以看Hollis大神这篇文章,写得真心好!为什么我墙裂建议大家使用枚举来实现单例。

八、EnumSet 和EnumMap

EnumSet

先来看看EnumSet的继承体系图

显然,EnumSet也实现了set接口,相比于HashSet,它有以下优点:

  • 消耗较少的内存

  • 效率更高,因为是位向量实现的。

  • 可以预测的遍历顺序(enum常量的声明顺序)

  • 拒绝加null

EnumSet就是set的高性能实现,它的要求就是存放必须是同一枚举类型。EnumSet的常用方法:

  • allof() 创建一个包含指定枚举类里所有枚举值的EnumSet集合

  • range() 获取某个范围的枚举实例

  • of() 创建一个包括参数中所有枚举元素的EnumSet集合

  • complementOf() 初始枚举集合包括指定枚举集合的补集

看实例,最实际:

  1. public class EnumTest {

  2. public static void main(String[] args) {

  3. // Creating a set

  4. EnumSet<SeasonEnum> set1, set2, set3, set4;


  5. // Adding elements

  6. set1 = EnumSet.of(SeasonEnum.SPRING, SeasonEnum.FALL, SeasonEnum.WINTER);

  7. set2 = EnumSet.complementOf(set1);

  8. set3 = EnumSet.allOf(SeasonEnum.class);

  9. set4 = EnumSet.range(SeasonEnum.SUMMER,SeasonEnum.WINTER);

  10. System.out.println("Set 1: " + set1);

  11. System.out.println("Set 2: " + set2);

  12. System.out.println("Set 3: " + set3);

  13. System.out.println("Set 4: " + set4);

  14. }

  15. }

输出结果:

  1. Set 1: [SPRING, FALL, WINTER]

  2. Set 2: [SUMMER]

  3. Set 3: [SPRING, SUMMER, FALL, WINTER]

  4. Set 4: [SUMMER, FALL, WINTER]

EnumMap

EnumMap的继承体系图如下:

EnumMap也实现了Map接口,相对于HashMap,它也有这些优点:

  • 消耗较少的内存

  • 效率更高

  • 可以预测的遍历顺序

  • 拒绝null

EnumMap就是map的高性能实现。 它的常用方法跟HashMap是一致的,唯一约束是枚举相关。

看实例,最实际:

  1. public class EnumTest {

  2. public static void main(String[] args) {

  3. Map<SeasonEnum, String> map = new EnumMap<>(SeasonEnum.class);

  4. map.put(SeasonEnum.SPRING, "春天");

  5. map.put(SeasonEnum.SUMMER, "夏天");

  6. map.put(SeasonEnum.FALL, "秋天");

  7. map.put(SeasonEnum.WINTER, "冬天");

  8. System.out.println(map);

  9. System.out.println(map.get(SeasonEnum.SPRING));

  10. }

  11. }

运行结果

  1. {SPRING=春天, SUMMER=夏天, FALL=秋天, WINTER=冬天}

  2. 春天

九、待更新

有关于枚举关键知识点,亲爱的朋友,你有没有要补充的呢?

参考与感谢

  • 关于Java中枚举Enum的深入剖析

  • 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题

  • 为什么我墙裂建议大家使用枚举来实现单例。

  • 深入理解Java枚举类型(enum)

  • 深入理解 Java 枚举

  • EnumSet in Java

  • Java 枚举(enum) 详解7种常见的用法

  • 《Java编程思想》

个人公众号

  • 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。

  • 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Java枚举
java enum(枚举)使用详解 + 总结AAA
Java枚举(enum)详解:Java声明枚举类型、枚举(enum)类、EnumMap 与 EnumSet
说说Java中的枚举(一)
为什么建议你使用枚举?
J2SE5.0专题之语言特性
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服