介绍
enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中。
使用关键字enum来定义枚举类,枚举类是一个特殊的类,大部分功能和普通类是一样的。
区别为:枚举类继承了java.lang.Enum类,而不是默认的Object类。
而java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable接口。
非抽象的枚举类默认会使用final修饰,因此不能派生子类
枚举类型使用的最常用类型就是枚举常量,下面通过一个简单的Demo来说明枚举的原理。
使用示例:
1 |
// 定义 public enum Color { BLACK, WHITE } // 使用 public class Main { public static void main(String[] args) { System.out.println(Color.BLACK); } } // 结果 // BLACK |
从简单的示例,不能看出枚举的特点和枚举的具体实现。
下面我们通过 jad工具来反编译Color类, 通过jad -sjava Color.class反编译出一份java文件:
1 |
public final class Color extends Enum <span class="token punctuation">{<!-- --></span> public <span class="token keyword">static</span> Color<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">values</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token punctuation">(</span>Color<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> $VALUES<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> public <span class="token keyword">static</span> Color <span class="token function">valueOf</span><span class="token punctuation">(</span>String name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token punctuation">(</span>Color<span class="token punctuation">)</span> Enum<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>em <span class="token operator">/</span> Color<span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> private <span class="token function">Color</span><span class="token punctuation">(</span>String name<span class="token punctuation">,</span> <span class="token keyword">int</span> ordinal<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">super</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> ordinal<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> public <span class="token keyword">static</span> final Color BLACK<span class="token punctuation">;</span> public <span class="token keyword">static</span> final Color WHITE<span class="token punctuation">;</span> private <span class="token keyword">static</span> final Color $VALUES<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token punctuation">{<!-- --></span> BLACK <span class="token operator">=</span> new <span class="token function">Color</span><span class="token punctuation">(</span><span class="token string">"BLACK"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> WHITE <span class="token operator">=</span> new <span class="token function">Color</span><span class="token punctuation">(</span><span class="token string">"WHITE"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> $VALUES <span class="token operator">=</span> <span class="token punctuation">(</span>new Color<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{<!-- --></span> BLACK<span class="token punctuation">,</span> WHITE <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
从反编译的类中,可以看出, 我们使用enum关键字编写的类,在编译阶段编译器会自动帮我们生成一份真正在jvm中运行的代码。
Color 类继承自 Enum类:
1 |
public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable |
Enum类接受一个继承自Enum的泛型.(在反编译java文件中没有体现泛型是因为,泛型在编译阶段就会被类型类型擦除(参考泛型详解,链接:泛型详解),替换为具体的实现)。
从反编译的Color类中可以看出,在enum关键字的类中,第一行 (准确的说是第一个分号前) 定义的变量,都会生成一个 Color实例,且它是在静态域中进行初始化的, 而静态域在类加载阶段的cinit中进行初始化,所以枚举对象是线程安全的,由JVM来保证.
生成的枚举类有 Color $VALUES[];成员变量,外部可以通过values()方法获取当前枚举类的所有实例对象。
通过上述查看编译后的枚举类后,我们可以清晰看到枚举类编译后的类内容信息,但是笔者仍旧抱有怀疑的态度,于是就用反射做了验证。
源码:
1 |
import java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span>Constructor<span class="token punctuation">;</span> import java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span>Field<span class="token punctuation">;</span> import java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span>Method<span class="token punctuation">;</span> public class Main <span class="token punctuation">{<!-- --></span> public <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Class clazz <span class="token operator">=</span> Color<span class="token punctuation">.</span>class<span class="token punctuation">;</span> Field<span class="token punctuation">[</span><span class="token punctuation">]</span> declaredFields <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getDeclaredFields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span>Field field <span class="token punctuation">:</span> declaredFields<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>field<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> Method<span class="token punctuation">[</span><span class="token punctuation">]</span> declaredMethods <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getDeclaredMethods</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>Method method <span class="token punctuation">:</span> declaredMethods<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> Constructor<span class="token punctuation">[</span><span class="token punctuation">]</span> constructors <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getConstructors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>constructors<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">enum</span> Color <span class="token punctuation">{<!-- --></span> BLACK<span class="token punctuation">,</span> WHITE <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
运行结果:
1 |
public static final Main$Color Main$Color.BLACK public static final Main$Color Main$Color.WHITE private static final Main$Color[] Main$Color.$VALUES public static Main$Color[] Main$Color.values() public static Main$Color Main$Color.valueOf(java.lang.String) 0 |
可以得知,编译过后的Color类,含三个常量,两个方法,没有可见的构造方法。
先说一个案例,你需要让每一个星期几对应到一个整数,比如星期天对应0。由于枚举类在定义的时候会自动为每个变量添加一个顺序,从0开始。
假如你希望0代表星期天,1代表周一,并且你在定义枚举类的时候,顺序也是这个顺序,那你可以不用定义新的变量,就像这样:
1 |
public <span class="token keyword">enum</span> Weekday <span class="token punctuation">{<!-- --></span> SUN<span class="token punctuation">,</span>MON<span class="token punctuation">,</span>TUS<span class="token punctuation">,</span>WED<span class="token punctuation">,</span>THU<span class="token punctuation">,</span>FRI<span class="token punctuation">,</span>SAT <span class="token punctuation">}</span> |
这个时候,星期天对应的ordinal值就是0,周一对应的就是1,满足你的要求。但是,如果你这么写,那就有问题了:
1 |
public enum Weekday { MON,TUS,WED,THU,FRI,SAT,SUN } |
我把SUN放到了最后,但是我还是希0代表SUN,1代表MON怎么办呢?默认的ordinal是指望不上了,因为它只会傻傻的给第一个变量0,给第二个1、、、
所以,我们需要自己定义变量!
看代码:
1 |
public <span class="token keyword">enum</span> Weekday <span class="token punctuation">{<!-- --></span> <span class="token function">MON</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">TUS</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">WED</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">THU</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">FRI</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">SAT</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">SUN</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> private <span class="token keyword">int</span> value<span class="token punctuation">;</span> private <span class="token function">Weekday</span><span class="token punctuation">(</span><span class="token keyword">int</span> value<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> this<span class="token punctuation">.</span>value <span class="token operator">=</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
我们对上面的代码做了一些改变:
首先,我们在每个枚举变量的后面加上了一个括号,里面是我们希望它代表的数字。
然后,我们定义了一个int变量,然后通过构造函数初始化这个变量。
你应该也清楚了,括号里的数字,其实就是我们定义的那个int变量。这句叫做自定义变量。
请注意:这里有三点需要注意:
一定要把枚举变量的定义放在第一行,并且以分号结尾。
构造函数必须私有化。事实上,private是多余的,你完全没有必要写,因为它默认并强制是private,如果你要写,也只能写private,写public是不能通过编译的。
自定义变量与默认的ordinal属性并不冲突,ordinal还是按照它的规则给每个枚举变量按顺序赋值。
好了,你已经掌握了上面的知识,你想,既然能自定义一个变量,能不能自定义两个呢?
当然可以:
1 |
public <span class="token keyword">enum</span> Weekday <span class="token punctuation">{<!-- --></span> <span class="token function">MON</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">"mon"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">TUS</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">"tus"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">WED</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token string">"wed"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">THU</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token string">"thu"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">FRI</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span><span class="token string">"fri"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">SAT</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">,</span><span class="token string">"sat"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">SUN</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token string">"sun"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> private <span class="token keyword">int</span> value<span class="token punctuation">;</span> private String label<span class="token punctuation">;</span> private <span class="token function">Weekday</span><span class="token punctuation">(</span><span class="token keyword">int</span> value<span class="token punctuation">,</span>String label<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> this<span class="token punctuation">.</span>value <span class="token operator">=</span> value<span class="token punctuation">;</span> this<span class="token punctuation">.</span>label <span class="token operator">=</span> label<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
每一个枚举类型极其定义的枚举变量在JVM中都是唯一的
这句话的意思是枚举类型它拥有的实例在编写的时候,就已经确定下,不能通过其他手段进行创建,且枚举变量在jvm有且只有一个对应的实例.