枚举类这个类用的比较少, 对这个不怎么熟悉, 最近看源码刚好可以好好了解一下, 那么, 枚举 Enum 是什么呢? 在 jdk 中, Enum 是一个抽象类下图所示, 这就说明这个类是不能进行实例化的, 那么我们应该怎么使用呢?
1. 枚举类的基本使用
简单的使用一下(随便借用的一个栗子), 我们可以直接把枚举类当作一个常量一样来使用这个类, 那么问题来了, 下面的这个枚举类和上面说的那么 Enum 抽象类是什么关系呢?
肯定是继承啊, 这里很像 Cglib 动态代理, 但肯定不是代理... 反正下面这个类经过编译, 就会编译出两个字节码文件, 一个是 TestEnum 类. 另外一个就是 Week 类(这个类自动继承 Enum 抽象类);
- public class TestEnum{
- public static void main(String[] args) {
- System.out.println(Week.MON);
- }
- }
- // 这个枚举也可以放在 TestEnum 里面, 充当一个静态内部类
- enum Week{
- MON,TUE,WED,THU,FRI,SAT,SUN
- }
我们可以反编译一下看看 Week 类中到底是些什么东西;
- // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
- // Jad home page: http://www.kpdus.com/jad.html
- // Decompiler options: packimports(3)
- // Source File Name: TestEnum.java
- // 自动的将枚举类 Week 继承 Enum
- final class Week extends Enum{
- public static final Week MON;
- public static final Week TUE;
- public static final Week WED;
- public static final Week THU;
- public static final Week FRI;
- public static final Week SAT;
- public static final Week SUN;
- private static final Week $VALUES[];
- // 静态代码块中会实例化七个对象和一个 Week 数组, 用于存放这些实例化对象
- static {
- MON = new Week("MON", 0);
- TUE = new Week("TUE", 1);
- WED = new Week("WED", 2);
- THU = new Week("THU", 3);
- FRI = new Week("FRI", 4);
- SAT = new Week("SAT", 5);
- SUN = new Week("SUN", 6);
- $VALUES = (new Week[] {
- MON, TUE, WED, THU, FRI, SAT, SUN
- });
- }
- // 构造器
- private Week(String s, int i){
- super(s, i);
- }
- // 生成静态方法 values, 克隆一份数组, 也就是调用这个方法之后就会返回一份包括枚举类中所有实例的数组
- public static Week[] values(){
- return (Week[])$VALUES.clone();
- }
- // 生成静态方法 valueOf, 通过指定一个枚举类型和实例的名称(字符串), 可以转化为一个该枚举类型的对象
- public static Week valueOf(String s){
- return (Week)Enum.valueOf(Week, s);
- }
- }
可以看到 Enum 虽然我们用的时候是用 Enum 来声明的, 但是实际上就是一个类, 是为了让我们用起来方便简洁, 才这样设计的(虽然我还是觉得枚举很怪...);
2. 看看 Enum 抽象类
我们发现生成的 Week 类中构造器会调用父类的构造器, 其中 i 表示每个实例在数组中的位置, 还有 values 和 valueof 方法也会调用父类的方法, 我们看看父类所有方法实现, 然后再回头看看就清楚了;
- package java.lang;
- import java.io.Serializable;
- import java.io.IOException;
- import java.io.InvalidObjectException;
- import java.io.ObjectInputStream;
- import java.io.ObjectStreamException;
- // 这是一个抽象类, 只能被继承
- public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
- // 枚举类中的实例名称
- private final String name;
- // 获取实例名称
- public final String name() {
- return name;
- }
- // 该实例在实例数组中的位置
- private final int ordinal;
- // 获取该实例所在位置
- public final int ordinal() {
- return ordinal;
- }
- // 此构造器只能由子类自己调用, 我们是不能调用, 看到子类 Week 中的构造器中的 super(s, i);
- protected Enum(String name, int ordinal) {
- this.name = name;
- this.ordinal = ordinal;
- }
- // 输出实例名称
- public String toString() {
- return name;
- }
- // 重写 equal 方法和 hashcode 方法, 由于枚举类中都是对象, 那么比较的肯定就是引用是不是一样了
- public final boolean equals(Object other) {
- return this==other;
- }
- public final int hashCode() {
- return super.hashCode();
- }
- // 克隆, 这里会直接报错, 子类中调用的是 Object 中的 clone 方法
- protected final Object clone() throws CloneNotSupportedException {
- throw new CloneNotSupportedException();
- }
- // 比较一个枚举类中实例名称的前后顺序
- public final int compareTo(E o) {
- Enum other = (Enum)o;
- Enum self = this;
- if (self.getClass() != other.getClass() &&
- self.getDeclaringClass() != other.getDeclaringClass())
- throw new ClassCastException();
- return self.ordinal - other.ordinal;
- }
- // 获取枚举实例的 Class 对象和父类的 Class 对象, 然后判断是不是一个对象, 我也不知道干嘛用的
- public final Class<E> getDeclaringClass() {
- Class clazz = getClass();
- Class zuper = clazz.getSuperclass();
- return (zuper == Enum.class) ? clazz : 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);
- }
- // 最没用的方法, Object 中也有
- 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");
- }
- }
这个抽象类方法也不是很多, 很容易, 现在再来看看枚举类应该就很容易了;
我们理一下思路: 当我们在一个平常的类中使用了枚举类的话, 而且这个枚举类中定义了很多个实例, 那么在使用的时候就会将枚举类拿出来和类分开编译, 这个枚举类中的多个实例 (这里每个实例都有自己的数组下标) 都给实例化出来, 并且放到一个实例数组中; 其中每个枚举类都会自动继承 Enum 这个抽象类, 我们可以根据对应的枚举类来获取每个实例的对象, 当然枚举类可能也有多个, 那么也可以根据 Enum 这个抽象类来获取对应枚举类中的实例, 并转化为该枚举类的类型返回...
原理就这么多吧! 下面来看看枚举类的一些其他用法;
3. 枚举类的简单用法
首先, 枚举类可以实现接口:
- public interface MyEnum {
- public void say();
- }
- // 实现接口
- public enum TestEnum implements MyEnum{
- // 注意,
- MON, TUE, WED, THU, FRI, SAT, SUN;
- @Override
- public void say() {
- System.out.println("實現接口 --------say");
- }
- public static void main(String[] args) {
- TestEnum.MON.say();
- }
- }
然后在枚举类中还能有一些属性, 以及 set/get 方法;
- package com.wyq.test;
- public enum TestEnum implements MyEnum{
- // 注意, 这里最后要加分号
- MON("mon",12), TUE("tue",12), WED("wed",12), THU("thu",12);
- private String name;
- private Integer age;
- private TestEnum(String name, Integer age) {
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Integer getAge() {
- return age;
- }
- public void setAge(Integer age) {
- this.age = age;
- }
- @Override
- public void say() {
- System.out.println("实现接口 --------say");
- }
- public static void main(String[] args) {
- System.out.println(TestEnum.MON.getName()+TestEnum.MON.getAge());
- }
- }
4. 总结
反正枚举类用的比较少, 我就在那个单例模式下用过(天生的单例), 至于其他的地方暂时用的比较少, 不过当需要用的时候我们一定要会用啊;
简单的看了看源码之后, 其实再回头看看枚举类, 其实就跟我们平常用的类差不多, 不可以继承(因为默认已经继承了 Enum 类了), 可以有自己的属性, 方法, 也可以覆盖父类的方法(可以自己试试), 就跟平常类一样的使用! 就是写法略怪, 习惯就好!!!
话说昨天下午弄了好久, 终于被我找到在博客园配置出了血小板看板娘了, 哈哈哈, 我现在声明一句: 血小板就是我老婆, 不接受反驳! 嘿嘿嘿 @_@
来源: https://www.cnblogs.com/wyq1995/p/11119397.html