JDK学习之Enum

enum的使用

在没有enum之前如果想要定义一些常量,就会采用如下的方式

假设要定义四个常量表示不同的季节

public class SeasonWithoutEnum {
public static final int spring=1;
public static final int summer=2;
public static final int autumn=3;
public static final int winter=4; public static void getSession(int a){
switch(a){
case SeasonWithoutEnum.spring:
System.out.println("春天!");
break;
case SeasonWithoutEnum.summer:
System.out.println("夏天!");
break;
case SeasonWithoutEnum.autumn:
System.out.println("秋天!");
break;
case SeasonWithoutEnum.winter:
System.out.println("winter is comming");
break;
default:
System.out.println("查无此季");
break;
}
} public static void main(String[] args) {
SeasonWithoutEnum.getSession(SeasonWithoutEnum.spring);
SeasonWithoutEnum.getSession(5);
}
}

缺点:

  1. 对于第一个调用,似乎没有什么问题也是常见的调用,但是第二个调用就存在这类型不安全的问题由于没有限制参数a的范围,导致随便传入一个数字都可以,如果还需要考虑这个数字的限制,那么代码的逻辑就会变得复杂

  2. 可读性差,对于上面的示例来说,使用了数字来表示季节,而我们通常使用String来进行季节的表示,如果我们使用String,虽然jdk现在提供了String的switch的支持,但是使用了String的hashcode做比较,还需要处理hash冲突,自然是比较麻烦的

对于上面的这种需求场景采用enum来改进

public enum Season {
SPRING(1,"春天!"),SUMMER(2,"夏天!"),AUTUMN(3,"秋天!"),WINTTER(4,"冬天!");
private int num;
private String sName;
Season(int num,String sName){
this.num=num;
this.sName=sName;
} public String getsName(){
return this.sName;
} public static void getSession(Season season){
switch(season){
case SPRING:
System.out.println(season.getsName());
break;
case SUMMER:
System.out.println(season.getsName());
break;
case AUTUMN:
System.out.println(season.getsName());
break;
case WINTTER:
System.out.println(season.getsName());
break;
}
} public static void main(String[] args) {
Season.getSession(Season.SPRING);
Season.getSession(Season.WINTTER);
}
}

此时通过season进行枚举,避免了类型安全问题,只能传入已有的枚举实例,此外,又提高了表意性

enum是如何实现的

对于以下的代码:

public enum Season{
SPRING,SUMMER;
}

就这么简简单单的几行代码怎么就定义了枚举呢?为什么又说枚举类呢?

通过jad反编译Season.class得到如下代码

jad反编译工具的下载链接如下:https://varaneckas.com/jad/

package com.hustdj.jdkStudy;

//实际上它就是继承自Enum的一个final类,也就是我们声明的enum就是一个继承自Enum的final类
public final class Season extends Enum
{ private Season(String s, int i)
{
super(s, i);
} //values方法,通过arraycoapy的方式返回所有枚举实例
public static Season[] values()
{
Season aseason[];
int i;
Season aseason1[];
System.arraycopy(aseason = ENUM$VALUES, 0, aseason1 = new Season[i = aseason.length], 0, i);
return aseason1;
} public static Season valueOf(String s)
{
return (Season)Enum.valueOf(com/hustdj/jdkStudy/Season, s);
} public static final Season SPRING;
public static final Season SUMMER;
private static final Season ENUM$VALUES[]; //静态代码块
static
{
SPRING = new Season("SPRING", 0);
SUMMER = new Season("SUMMER", 1);
ENUM$VALUES = (new Season[] {
SPRING, SUMMER
});
}
}

结论

  1. enum关键字的背后,是编译器为我们做了事情,它其实是一个继承了Enum的final类,自然它就不能再被继承
  2. 我们所声明的枚举实例,实际上也是由静态代码块为我们创建的
  3. 有一个隐藏的方法values(),它提供所有的枚举实例对象的拷贝,有意思的是这个方法你是追踪不到的,也就是说你用ctrl是追踪 不进去的,原因就是它是在编译期生成的方法

Enum

既然知道了enum是通过继承了Enum的final类,那么就来看看Enum这个超类吧

public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable { private final String name; public final String name() {
return name;
}
//优先级,这个默认从0开始累加,从反编译中可以看出来
private final int ordinal; public final int ordinal() {
return ordinal;
} protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
} public String toString() {
return name;
}
//对于enum来说==和equals的作用相同,因为equals使用的就是==
public final boolean equals(Object other) {
return this==other;
} public final int hashCode() {
return super.hashCode();
}
//不允许使用clone方法
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
//compareTo比较的是ordinal
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
} @SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
} public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
} protected final void finalize() { } private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
} private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}

有以下几个点需要注意:

  1. enum的==与equals方法等效
  2. enum的排序使用的ordinal成员变量的大小排序
  3. enum禁止使用clone方法,会直接抛出异常

Switch对于enum的支持

对于上面enum使用的中的例子,反编译得到的代码如下:

public static void getSession(Season season)
{
switch($SWITCH_TABLE$com$hustdj$jdkStudy$Season()[season.ordinal()])
{
case 1: // '\001'
System.out.println("\u6625\u5929\uFF01");
break; case 2: // '\002'
System.out.println("\u590F\u5929\uFF01");
break; case 3: // '\003'
System.out.println("\u79CB\u5929\uFF01");
break; case 4: // '\004'
System.out.println("\u51AC\u5929\uFF01");
break;
}
} static int[] $SWITCH_TABLE$com$hustdj$jdkStudy$Season()
{
$SWITCH_TABLE$com$hustdj$jdkStudy$Season;
if($SWITCH_TABLE$com$hustdj$jdkStudy$Season == null) goto _L2; else goto _L1
_L1:
return;
_L2:
JVM INSTR pop ;
int ai[] = new int[values().length];
try
{
ai[AUTUMN.ordinal()] = 3;
}
catch(NoSuchFieldError _ex) { }
try
{
ai[SPRING.ordinal()] = 1;
}
catch(NoSuchFieldError _ex) { }
try
{
ai[SUMMER.ordinal()] = 2;
}
catch(NoSuchFieldError _ex) { }
try
{
ai[WINTTER.ordinal()] = 4;
}
catch(NoSuchFieldError _ex) { }
return $SWITCH_TABLE$com$hustdj$jdkStudy$Season = ai;
}

通过查看反编译的代码可以看到,$SWITCH_TABLE$com$hustdj$jdkStudy$Season方法把enum的ordinal自定义的num关联起来返回数组,在switch时通过enum的ordinal获取到对应的num,然后再switch,比较的最终还是自定义的num

常用枚举TimeUnit

public enum TimeUnit {
NANOSECONDS {
public long toNanos(long d) { return d; }
public long toMicros(long d) { return d/(C1/C0); }
public long toMillis(long d) { return d/(C2/C0); }
public long toSeconds(long d) { return d/(C3/C0); }
public long toMinutes(long d) { return d/(C4/C0); }
public long toHours(long d) { return d/(C5/C0); }
public long toDays(long d) { return d/(C6/C0); }
public long convert(long d, TimeUnit u) { return u.toNanos(d); }
int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
},
/*还有很多就不一一列举了*/
}

非常的amazing啊,在实例NANOSECONDS中它重写了TimeUnit中定义的方法,这个可以通过反编译查看,在老朋友season中试了一下,效果如下

//源代码
SPRING(1,"春天!"){
public String getsName(){
return "春天!重载";
}
},
//反编译
static
{
SPRING = new Season("SPRING", 0, 1, "\u6625\u5929\uFF01") { public String getsName()
{
return "\u6625\u5929\uFF01\u91CD\u8F7D";
} }
;
SUMMER = new Season("SUMMER", 1, 2, "\u590F\u5929\uFF01");
AUTUMN = new Season("AUTUMN", 2, 3, "\u79CB\u5929\uFF01");
WINTTER = new Season("WINTTER", 3, 4, "\u51AC\u5929\uFF01");
ENUM$VALUES = (new Season[] {
SPRING, SUMMER, AUTUMN, WINTTER
});
}

枚举与单例

【回头再好好补充】

JDK阅读之Enum的更多相关文章

  1. jdk阅读xml文件

    前言 你需要阅读的时间来写一个通用组件xml文件,但考虑到组件分布更容易,这样一来在第三方小引用jar包.因此,直接jdk内建的xml分析方法.可能都没有第三发的组件强大. 导入的文件: import ...

  2. Java _ JDK _ Arrays, LinkedList, ArrayList, Vector 及Stack

    (最近在看JDK源码,只是拿着它的继承图在看,但很多东西不记录仍然印象不深,所以开始记录JDK阅读系列.) (一)Arrays Arrays比较特殊,直接继承自Arrays ->List(Int ...

  3. 【转】java枚举类型enum的使用

    原文网址:http://blog.csdn.net/wgw335363240/article/details/6359614 java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到 ...

  4. java枚举类型enum的使用

    2015-10-24 java达人 Java 中 的枚举类型采用关键字enum 来定义,从jdk1.5才有的新类型,所有的枚举类型都是继承自Enum 类型.要了解枚举类型,建议大家先打开jdk 中的E ...

  5. java中enum类型的使用

    java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public final static 类型来定义呢?以前我 ...

  6. 转载 java枚举类型enum的使用 (原文地址:http://blog.csdn.net/wgw335363240/article/details/6359614)

    java枚举类型enum的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java中定义的常量值不采用enmu枚举类型,而采用public final static 类型来定义呢?以前我们都是采 ...

  7. java 枚举类型enum

    简单介绍 Java 中的枚举类型采用关键字enum 来定义,从jdk1.5才有的新类型,所有的枚举类型都是继承自Enum 类型.要了解枚举类型,建议大家先打开jdk 中的Enum 类简单读一下,这个类 ...

  8. 【译】Core Java Questions and Answers【1-33】

    前言 译文链接:http://www.journaldev.com/2366/core-java-interview-questions-and-answers Java 8有哪些重要的特性 Java ...

  9. Python 中的枚举类型~转

    Python 中的枚举类型 摘要: 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期.月份.状态等. 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表 ...

随机推荐

  1. python使用pandas进行数据处理

    pandas数据处理 关注公众号"轻松学编程"了解更多. 以下命令都是在浏览器中输入. cmd命令窗口输入:jupyter notebook 打开浏览器输入网址http://loc ...

  2. codeforces 1442 A. Extreme Subtraction(贪心,构造)

    传送门 样例(x): 8 15 16 17 19 27 36 29 33 结果(t1) 15 15 16 18 26 35 28 32 思路:我们可以把最左端和最右端当做两个水龙头,每个水龙头流量的上 ...

  3. LWJGL3的内存管理,第三篇,剩下的两种策略

    LWJGL3的内存管理,第三篇,剩下的两种策略 上一篇讨论的基于 MemoryStack 类的栈上分配方式,是效率最高的,但是有些情况下无法使用.比如需要分配的内存较大,又或许生命周期较长.这时候就可 ...

  4. python开发基础(二)运算符以及数据类型之list(列表)

    # encoding: utf-8 # module builtins # from (built-in) # by generator 1.147 """ Built- ...

  5. SQL2005数据库可疑的解决方法

    sqlserver数据库标注为可疑的解决办法 一般引起可疑的原因是突然断电,服务器死机,强制关机导致正在运行的数据库文件损坏,需要进行修复.方法一:USE MASTER GOSP_CONFIGURE ...

  6. 剑指29:最小的k个数

    题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4. class Solution {public:    vector& ...

  7. C++ 基础 5:多态

    1 什么是多态 多态按字面的意思就是多种形态.当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态. C++ 多态意味着由继承而产生的相关的不同的类,调用重写函数时,会根据实际的对象类型来执 ...

  8. TCP/IP协议与Socket

    1.计算机网络体系结构分层 OSI 参考模型注重"通信协议必要的功能是什么", TCP/IP 则更强调"在计算机上实现协议应该开发哪种程序". 2.TCP/IP ...

  9. 手把手教你使用rpm部署ceph集群

    环境准备 1.在运行 Ceph 守护进程的节点上创建一个普通用户,ceph-deploy 会在节点安装软件包,所以你创建的用户需要无密码 sudo 权限.如果使用root可以忽略. 为赋予用户所有权限 ...

  10. CSS兼容性总结一点点

    CSS3的兼容性,除了前缀.还有参数格式的区分,因为仍在变化中,不在这篇文章中讨论. 很想总结一下IE 6 7 8 9 10的兼容性问题,但是我实在不喜欢IE 6 7 8,在Web开发上也很少再调整到 ...