Java枚举知识点整理总结

枚举 enum 是JDK1.5加入的特性,平时编码中,可能更多的用 public static final 的修饰来表示不同类型的常量,当然,枚举以其直观,规范等特点,也不失为一个好的选择。总结一下枚举的用法,方便以后查看。总之首先要记住的就是,枚举是一个类

枚举基础

一个简单的枚举类:

public enum Dream {

    WIND, FLOWER, SNOW, MOON

}

所有枚举都是 java.lang.Enum 的子类

枚举本质上也是一个类,他只是编译器的一个语法糖,全天下所有枚举都是java.lang.Enum
的子类,也就是说,枚举类能像普通类一样添加成员变量和方法。

在枚举类 Dream 中,WIND, FLOWER, SNOW, MOON 等值实际上就是 java.lang.Enum 类的实例,跟接口一样,默认使用 public static final 修饰,每一个实例都是默认调用了 java.lang.Enum 中的构造方法实现的:

protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

其中,name 就是枚举中值的名字,如 WINDordinal 表示从 0 开始定义的顺序,如 WIND 的顺序就为 0MOON 的顺序为 3

枚举类的私有构造方法

枚举类中的构造方法只能是私有 private 的,强行添加非私有的构造方法,则无法通过编译。这说明,枚举类是不能被继承的,默认地,枚举类都是final的,从枚举类存在的意义来看,这也是理所当然的。当然,枚举类本身也不能继承其他类,因为已经默认继承了 java.lang.Enum 类。

和普通类一样,枚举类中有一个默认的无参构造方法,当为枚举添加其他的构造方法时,默认的构造方法将失效。此时,在枚举中初始化值时,应显式调用构造方法:

public enum Dream {

    WIND, FLOWER, SNOW(666), MOON(999);

    int growthRing;

    Dream() {
        System.out.println(this.name() + " 被构造啦"); //方法name()是父类中的方法,返回当前实例名字
    }

    Dream(int growthRing) {
        this.growthRing = growthRing;
        System.out.println(this.growthRing + "圈");
    }

}

通过 Class.forName 方法加载枚举类测试:

public static void main(String[] args) {
    try {
        Class.forName("com.gdut.topic.enum_demo.Dream");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

结果如下:

WIND 被构造啦
FLOWER 被构造啦
构造 666圈
构造 999圈

可见,四个实例分别调用了四次构造函数。

枚举类中的方法

枚举类中,除了可以像普通类中添加自定义的方法和成员变量,自身也默认实现了两个方法,他们分别是 values 方法和 valueOf 方法。使用javap反编译Dream.class 文件得到如下代码:

public final class com.gdut.topic.enum_demo.Dream extends java.lang.Enum<com.gdut.topic.enum_demo.Dream> {
  public static final com.gdut.topic.enum_demo.Dream WIND;
  public static final com.gdut.topic.enum_demo.Dream FLOWER;
  public static final com.gdut.topic.enum_demo.Dream SNOW;
  public static final com.gdut.topic.enum_demo.Dream MOON;
  public static com.gdut.topic.enum_demo.Dream[] values();
  public static com.gdut.topic.enum_demo.Dream valueOf(java.lang.String);
  static {};
}

可以看到,除了对应枚举值的常量对象外,还有两个方法,并且他们是静态的。

values 方法返回一个包含全部枚举值的数组。如:

Dream[] dreams = Dream.values();

valueOf 方法根据名字返回枚举实例。如:

Dream SNOW = Dream.valueOf("SNOW");

父类中的方法

枚举类的父类 java.lang.Enum 中也有许多方法,最有用的是 toString 方法,他和 name 方法一样,返回枚举常量名。例如Dream.FLOWER.toString 将返回字符串 "FLOWER"

java.lang.Enum 的静态方法 valueOftoString 的逆方法,他接收一个枚举类型的 class 对象和枚举常量的名字 name,返回具体的枚举常量。如:

Dream MOON = Enum.valueOf(Dream.class, "MOON");

namevalueOf 方法其实就是JVM提供的对枚举的序列化与反序列化方法。

java.lang.Enum 还实现了 java.lang.Comparable 接口,即实现了 compareTo 方法,具体实现如下:

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;
}

看的出来,只是简单比较两个枚举常量定义时的顺序,并且不能重写这个方法。

抽象方法

枚举中可以定义抽象方法,当枚举类定义抽象方法或者实现接口时,就是抽象类,但不用显式的添加 abstract 关键字,由于枚举类无法继承,所以需要在实例化枚举常量时实现抽象方法,写法就跟匿名内部类的写法一样:

public enum Dream {

    WIND{
        @Override
        public int getYear() {
            return 10;
        }
    },
    FLOWER{
        @Override
        public int getYear() {
            return 20;
        }
    },
    SNOW{
        @Override
        public int getYear() {
            return 30;
        }
    },
    MOON{
        @Override
        public int getYear() {
            return 40;
        }
    };

    public abstract int getYear();

}

有人说,这也是算是通过特殊的方法继承了枚举类了。

枚举用处

这是最直观的用处,相比于常量接口,具有直观规范的特点,比如有一个接收星期数的方法,用常量接口,可能传的值是0-6,但不能避免不小心传到其他的值,使用枚举作为参数,则不会出现这个问题,即可控性天然得到了保证。

枚举还可以用来作为 switch 语句的参数使用。

枚举类的 equals 方法,直接用 == 操作符进行比较,并且不能重写。

用枚举实现单例

单元素枚举可用来实现单例,因为枚举中的实例都是被 static final 修饰的常量,只实例化一次,又因为他全部私有的构造方法,保证了无法从外部实例对象。

Java规范又指出,在枚举的序列化与反序列化上,是通过 namevalueOf 方法来实现的,也就是序列化枚举的名字,反序列化时根据名字查找对象。因此反序列化后的实例也会和之前被序列化的对象实例相同。

因此,用枚举实现单例,不失为一种好方法:

public enum Singleton {

    INSTANCE;

    public void doSomeThing() {
        //...
    }

}

总结

  1. 所有枚举都是 java.lang.Enum的子类,枚举可以有成员变量和方法
  2. 枚举中的实例默认使用 public static final 修饰,并显示或隐式地调用构造方法初始化
  3. 枚举中的枚举常量应该写在枚举类中的开头
  4. 枚举只能有 private 的构造方法
  5. 枚举不能被继承,不能继承其他类,但可以实现接口,添加抽象方法
  6. 当枚举中有抽象方法时,枚举常量必须用类似匿名内部类的写法初始化
  7. 枚举的序列化和反序列化是用 namevalueOf 方法,写入枚举名字或根据枚举名字查找对象实现的
  8. 根据枚举的特性,单元素枚举可以实现类似饿汉式的单例模式

猜你喜欢

转载自www.cnblogs.com/gdutzyh/p/9266199.html