zoukankan      html  css  js  c++  java
  • Inside Enum 规格严格

     

    1 Introduction to enum
        Java SE 5中引入了枚举,同时添加了一个新关键字enum。下面是个枚举的例子:

    public enum Suit {
     CLUBS, DIAMONDS, HEARTS, SPADES;
    }    枚举类型也是普通的Java类,继承自java.lang.Enum并默认实现了java.lang.Comparable接口和 java.io.Serializable接口。所有的枚举类型都是final类,枚举值都是public static final,由于枚举值是常量,因此枚举值的名称通常应该大写。
         枚举类型也可以声明构造函数(只能是私有或者包级私有)、成员变量和成员方法,此外也能实现接口。需要注意的是,成员变量和成员方法的声明必须放在所有枚举值定义的后面,例如:

    public enum Status {
     //
     Normal("normal"), Warning("warning"), Error("error"), Fatal("fatal");
     
     //
     private String description;
     
     private Status(String description) {
      this.description = description;
     }
     
     public String getDescription() {
      return description;
     }

     public void setDescription(String description) {
      this.description = description;
     }
    }    Enum声明了name()方法和oridinal()方法,分别用于返回枚举值的名称和该枚举值在枚举类型中声明的顺序(从0开始),例如NORMAL.ordinal()返回0。Enum改写了toString方法,返回枚举值的名称。下面是个简单的例子:

    public static void main(String args[]) {
     //
     System.out.println("super class: " + Status.class.getSuperclass());
     
     //
     System.out.println(Status.valueOf("Normal").getDescription());
     
     //
     for(Status s: Status.values()) {
      System.out.println(s + ":" + s.ordinal());
     }
     
     //
     Status status = Status.Normal;
     switch(status) {
     case Normal:
      System.out.println("Status.Normal");
      break;
     case Warning:
      System.out.println("Status.Warning");
      break;
     case Error:
      System.out.println("Status.Error");
      break;
     case Fatal:
      System.out.println("Status.Fatal");
      break;
     default:
      System.out.println("unknown");
         break;
     }
     
     //
     EnumMap<Status, String> map = new EnumMap<Status, String>(Status.class);
     map.put(Status.Normal, "normal status");
     map.put(Status.Warning, "warning status");
     map.put(Status.Error, "error status");
     map.put(Status.Fatal, "fatal status");
     
     //
     EnumSet<Status> set = EnumSet.of(Status.Normal, Status.Warning);
     for(Status s : set) {
      System.out.println(s);
     }
    }    以上程序的输出如下。需要注意的是,case语句只需将其写成 case Normal 即可,也就是说不必写成 case Status.Normal,实际上如果写成Status.Normal,那么会导致编译错误。

    super class: class java.lang.Enum
    normal
    Normal:0
    Warning:1
    Error:2
    Fatal:3
    Status.Normal
    Normal
    Warning2 Inside java.lang.Enum
    2.1 Instantiation

        首先java.lang.Enum类的签名如下,其中<E extends Enum<E>>是递归类型限制,主要目的是提供compareTo(E e) 而不是compareTo(Enum e)。

    public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable    java.lang.Enum中唯一的构造函数如下,所有的成员变量都在构造函数内初始化。由于所有的成员变量都是不可变类型或者基本类型,因此没有额外的保护性拷贝。构造之后,所有的成员变量便处于只读状态。需要注意的是,java.lang.Enum的子类不一定是不可变类(虽然通常应该是不可变类),因为程序中定义的枚举类型可以包含可变的成员变量。

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }    java.lang.Enum类提供了一个静态工具方法,用于获得某个枚举类型中,名为name的枚举值。该方法通过调用java.lang.Class 的 enumConstantDirectory()方法获得该枚举类型所有枚举值的map。java.lang.Class的 enumConstantDirectory()方法内又通过反射调用了该枚举类型的values()方法获得所有的枚举值。需要注意的是,java.lang.Enum中并没有一个名为values()的静态方法,这个方法是编译器在编译枚举类型时添加的。

    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 const " + enumType +"." + name);

    2.2 Equality

    public final boolean equals(Object other) {
        return this==other;
    }

    public final int hashCode() {
        return super.hashCode();
    }    从以上代码中可以看出,对于枚举值的相等性判断,只需要判断引用是否相等即可。需要注意的是,这是在充分考虑了反射、对象克隆和序列化等诸多因素之后作出的决定。

        java.lang.reflect.Constructor的newInstance()方法中有如下代码,禁止了通过反射构造枚举对象:

    if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        throw new IllegalArgumentException("Cannot reflectively create enum objects"); 
    2.3 Comparison
        以下是跟枚举比较相关的代码:

    public final Class<E> getDeclaringClass() {
        Class clazz = getClass();
        Class zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? clazz : zuper;
    }

    public final int compareTo(E o) {
        Enum other = (Enum)o;
        Enum self = this;
        if (self.getClass() != other.getClass() && // optimization
                self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }    假设程序中定义的所有枚举类型都是继承自java.lang.Enum,而且所有的枚举类型都被声明为final类,这是否意味着所有枚举值的super class都应该是java.lang.Enum?为什么在getDeclaringClass()方法内会有对super class的判断呢?分析以下例子:

    public enum Operation {
        PLUS { double eval(double x, double y) { return x + y; } },
        MINUS { double eval(double x, double y) { return x - y; } },
        TIMES { double eval(double x, double y) { return x * y; } },
        DIVIDE { double eval(double x, double y) { return x / y; } };

        abstract double eval(double x, double y);
       
        public static void main(String args[]) {
            System.out.println(Operation.TIMES.getClass());
            System.out.println(Operation.TIMES.getClass().getSuperclass());
            System.out.println(Operation.TIMES.getDeclaringClass());
        }
    }    以上程序的输出如下:

    class Operation$3
    class Operation
    class Operation    从这个例子可以看出,并不是所有的枚举值的super class都是Emum.class。在compareTo()方法内需要判断getDeclaringClass()和ordinal是否相等。就像其注释中说明的那样,判断getClass()是否相等只是一种优化。

        此外,java.lang.Class类中包含以下代码,用于判断一个类是否是枚举类型。需要注意的是,这里没有使用getDeclaringClass(),而是直接使用getSuperclass()进行判断。

    public boolean isEnum() {
        // An enum must both directly extend java.lang.Enum and have
        // the ENUM bit set; classes for specialized enum constants
        // don't do the former.
        return (this.getModifiers() & ENUM) != 0 &&
        this.getSuperclass() == java.lang.Enum.class;
    }2.4 Clone
        以下是java.lang.Enum类的clone()方法,该方法中直接抛出CloneNotSupportedException异常,这保证了 java.lang.Enum无法被克隆,从而保证了枚举值的单例性(通常情况下,只有一个继承链上的除了java.lang.Object之外的所有类都在clone()方法内返回通过调用super.clone()方法返回的对象,那么才能保证clone的正确性)。

    protected final Object clone() throws CloneNotSupportedException {
            throw new CloneNotSupportedException();
    }2.5 Serialization
        java.lang.Enum类中序列化相关的代码如下:

    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");
    }    java.lang.Enum类实现了Serializable接口,但是却在readObject()和readObjectNoData()方法内直接抛出异常(如果某个类因为继承的原因实现了Serializable接口,而该类却不希望被序列化/反序列化,那么通常可以考虑在 readObject()和writeObject()方法中直接抛出异常)。
        实际上,Java的序列化机制对于枚举类型有特殊的处理,即没有使用普通对象的序列化形式:尽管java.lang.Enum中的name和ordial 成员变量都没有声明为transient,实际上序列化过程中写入流的只有name;反序列化过程中通过调用 Enum.valueOf(Class<T> enumType, String name)静态方法构造枚举值,从而保证了枚举值的单例性。


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/booboo2006/archive/2010/10/19/5950467.aspx

     

  • 相关阅读:
    2020面试阿里,字节跳动90%被问到的JVM面试题
    mysql数据库误删恢复
    因用了Insert into select语句,公司报警了
    Spring的Controller是单例还是多例?怎么保证并发的安全
    SpringBoot项目优化和Jvm调优
    java从一个pdf中取出指定页生成一个新的pdf
    java截取出字符串中的所有组数字
    oracle表空间位置迁移
    solr常用操作及集成分词器或cdh集群部署说明
    Oracle中将列查询结果多行逗号拼接成一个大字段
  • 原文地址:https://www.cnblogs.com/diyunpeng/p/1987121.html
Copyright © 2011-2022 走看看