JDK源码学习笔记——Enum枚举使用及原理
一、为什么使用枚举
什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数、一年四季等。或者是在我们编译前就知道其包含的所有值的集合。
利用 public final static 完全可以实现的功能,为什么要使用枚举?
public class Season {public static final int SPRING = 1;public static final int SUMMER = 2;public static final int AUTUMN = 3;public static final int WINTER = 4; }
(1)安全性。这种模式不是类型安全的。比如说我们设计一个函数,要求传入春夏秋冬的某个值。但是使用int类型,我们无法保证传入的值为合法。如:传入5。
(2)可读性。我们需要方便得到枚举类型的字符串表达式。int常量打印出来,我们所见到的就是一组数字,没什么用;String常量可以打印出详细信息,但是字符串的比较操作性能较低。
二、枚举的几种使用方式
用法一:常量
public enum Color { RED, GREEN, BLANK, YELLOW }
用法二:switch
enum Signal { GREEN, YELLOW, RED } public class TrafficLight { Signal color = Signal.RED; public void change() { switch (color) { case RED: color = Signal.GREEN; break; case YELLOW: color = Signal.RED; break; case GREEN: color = Signal.YELLOW; break; } } }
用法三:向枚举中添加新方法
public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } // 普通方法 public static String getName(int index) { for (Color c : Color.values()) { if (c.getIndex() == index) { return c.name; } } return null; } // get set 方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } }
用法四:覆盖枚举的方法
public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } //覆盖方法 @Override public String toString() { return this.index+"_"+this.name; } }
用法五:实现接口
public interface Behaviour { void print(); String getInfo(); } public enum Color implements Behaviour{ RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this.name = name; this.index = index; } //接口方法 @Override public String getInfo() { return this.name; } //接口方法 @Override public void print() { System.out.println(this.index+":"+this.name); } }
用法六:使用接口组织枚举
public interface Food { enum Coffee implements Food{ BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO } enum Dessert implements Food{ FRUIT, CAKE, GELATO } }
三、原理(代码易懂,不做详细介绍)
enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }// 反编译之后如下: // 反编译Day.class final class Day extends Enum {//编译器为我们添加的静态的values()方法public static Day[] values(){return (Day[])$VALUES.clone();}//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法public static Day valueOf(String s){return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);}//私有构造函数private Day(String s, int i){super(s, i);}//前面定义的7种枚举实例public static final Day MONDAY;public static final Day TUESDAY;public static final Day WEDNESDAY;public static final Day THURSDAY;public static final Day FRIDAY;public static final Day SATURDAY;public static final Day SUNDAY;private static final Day $VALUES[];static { //实例化枚举实例MONDAY = new Day("MONDAY", 0);TUESDAY = new Day("TUESDAY", 1);WEDNESDAY = new Day("WEDNESDAY", 2);THURSDAY = new Day("THURSDAY", 3);FRIDAY = new Day("FRIDAY", 4);SATURDAY = new Day("SATURDAY", 5);SUNDAY = new Day("SUNDAY", 6);$VALUES = (new Day[] {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY});} }
四、枚举保证线程安全
由上面反编译代码可以看到,“ public static final Day MONDAY; ”,static类型的属性会在类被加载之后被初始化,当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。
JVM类加载机制中:
“ 并发:
虚拟机会保证一个类的类构造器<clinit>()在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器<clinit>(),其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
特别需要注意的是,在这种情形下,其他线程虽然会被阻塞,但如果执行<clinit>()方法的那条线程退出后,其他线程在唤醒之后不会再次进入/执行<clinit>()方法,因为在同一个类加载器下,一个类型只会被初始化一次。 ”
参考资料 / 相关推荐:
Java 7 源码学习系列(二)——Enum
Java基础知识——枚举
深入理解Java枚举类型(enum)
【JAVA】浅谈java枚举类
Java 枚举(enum) 详解7种常见的用法
转载于:https://www.cnblogs.com/hexinwei1/p/9606266.html
JDK源码学习笔记——Enum枚举使用及原理相关推荐
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
- JDK源码学习笔记——String
1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() {i ...
- JDK源码学习笔记——TreeMap及红黑树
找了几个分析比较到位的,不再重复写了-- Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例 [Java集合源码剖析]TreeMap源码剖析 java源码分析之TreeMap基础篇 ...
- Apache log4j-1.2.17源码学习笔记
(1)Apache log4j-1.2.17源码学习笔记 http://blog.csdn.net/zilong_zilong/article/details/78715500 (2)Apache l ...
- RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?
RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...
- JDK源码学习-基础
JDK源码学习 目录 基础 1. 安装 1.1 下载JDK 1.2 配置环境变量 1.3 验证 2. 简单的程序 2.1 编写代码 2.2 编译文件 2.3 执行类 3. java基本类型 基础 1. ...
- Java多线程之JUC包:Semaphore源码学习笔记
若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...
- Vuex 4源码学习笔记 - 通过Vuex源码学习E2E测试(十一)
在上一篇笔记中:Vuex 4源码学习笔记 - 做好changelog更新日志很重要(十) 我们学到了通过conventional-changelog来生成项目的Changelog更新日志,通过更新日志 ...
- Vuex 4源码学习笔记 - Vuex是怎么与Vue结合?(三)
在上一篇笔记中:Vuex源码学习笔记 - Vuex开发运行流程(二) 我们通过运行npm run dev命令来启动webpack,来开发Vuex,并在Vuex的createStore函数中添加了第一个 ...
最新文章
- 交叉验证分析每一折(fold of Kfold)验证数据的评估指标并绘制综合ROC曲线
- torch.bmm()函数的使用
- SAP Hybris Enterprise Commerce的一些有用链接 - 保证持续更新
- 带你深入浅出的分析 HashTable 源码
- ASM的基础使用 Android 自动化埋点方案原理剖析
- Thinkphp5中session用法
- 【备忘】Aegisub字幕制作简易方法
- 如何在github上创建自己的个人网站
- 用笔记本创建wifi热点
- java调用百望税控NISEC_SKSC.dll发送xml报文
- 用于实时操作系统固件更新开发指南的MediaTek Linkit MT2523开发平台
- echarts在中国地图上绘制各省指标
- 二、Git本地仓库基本操作——创建Git仓库、提交更新或删除文件
- css3-z1,z-index
- 融合多策略的改进麻雀搜索算法-附代码
- [AHK]Bing Desktop Wallpaper Changer(必应壁纸随心换)
- 用计算机播放vcd教案,六年级信息技术下册 第六课制作班级VCD1教案 华中师大版...
- React 小记 start脚本
- php搭建markdown云笔记_使用 Leanote 搭建自己专属的云笔记
- 我是小R,昨晚我好像把B站搞崩了!
热门文章
- matlab实验函数编写与程序设计,matlab实验四函数编写与程序设计
- mysql查看服务器版本sql_云服务器Windows系统查看mysql版本
- java对外发布接口文档_java之接口文档规范
- php mysql删除失败_php mysql删除的代码哪里出错了?
- archives_do.php,织梦后台文章管理中增加批量添加tag标签功能
- oracle中存储过程和函数有什么区别,Oracle中存储过程和函数的区别
- python升级命令debian_debian python 2.7.11 升级
- oracle rman备份整库,RMAN备份恢复整个库
- linux内核 默认路由表,[Linux] linux路由表-Go语言中文社区
- mysql error log清理_手动删除mysql日志/var/log/mysql/error.log导致的mysql无法启动