Categories
Uncategorized

[Basic] things about Java enum class you may not know

table of Contents

  • 谈谈枚举

      1. Definition of enumeration class

      2. The implementation of the underlying enumeration class

      3. The sequence of enumeration class implemented

      4. enumeration achieve single

      5. The process of creating enumerate instances are thread-safe

Enumeration talk


If the number of objects of a class is limited and is constant, we usually such a class designed to be enumerated classes.


1. Definition of enumeration class

Enum class has the following characteristics:

    Enumeration class default is to use the keyword final modification, so enumeration class can not be inherited;

    Enumeration class constructor default is to use private modified;

    All in all instances must be listed in the first line defining enumeration class;

    Enum class can also implement interfaces;

    Enumeration class can contain abstract methods.

//默认final修饰,不能被继承
public enum  EnumDemo implements Runnable {

    //枚举的字段必须加注释
    //男性
    MALE("male"){
        @Override
        //这边每个枚举类都单独实现了接口方法,也可以统一实现
        //在枚举类的定义中实现一个就好了
        public void run() {
            System.out.println("l like run...");
        }

        @Override
        public void tellSex() {
            System.out.println("l am a man");
        }
    },
    //女性
    Female("female"){
        @Override
        public void run() {
            System.out.println("l hate running...");
        }

        @Override
        public void tellSex() {
            System.out.println("l am a girl");
        }
    };

    private String sex;

    public String getSex(){
        return sex;
    }

    /**
     * 构造函数默认是priva的
     * @param sex
     */
    EnumDemo(String sex){
        this.sex = sex;
    }

    /**
     * 抽象方法,需要枚举类实例实现这个方法
     */
    public abstract void tellSex();
}

2. The implementation of the underlying enumeration class

If there is an enumeration defined as follows class

public enum T {
    SPRING,SUMMER,AUTUMN,WINTER;
}

After a Java compiler, decompile class file if we can see the following code:

public final class T extends Enum
{
    private T(String s, int i)
    {
        super(s, i);
    }
    public static T[] values()
    {
        T at[];
        int i;
        T at1[];
        System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);
        return at1;
    }
 
    public static T valueOf(String s)
    {
        return (T)Enum.valueOf(demo/T, s);
    }
 
    public static final T SPRING;
    public static final T SUMMER;
    public static final T AUTUMN;
    public static final T WINTER;
    private static final T ENUM$VALUES[];
    static
    {
        SPRING = new T("SPRING", 0);
        SUMMER = new T("SUMMER", 1);
        AUTUMN = new T("AUTUMN", 2);
        WINTER = new T("WINTER", 3);
        ENUM$VALUES = (new T[] {
            SPRING, SUMMER, AUTUMN, WINTER
        });
    }
}

After compiling you can see, an enumeration class is a plain Java classes. This class inherits from the Enum class, and use the final modification, so enumeration class are not inherited. Enumeration enum values ​​are defined in the class member variable enumeration static class initialization code block and a one-time initialization, the value in the array. We call the values ​​enumeration class () method can get the all-time enumeration value. About enumeration type, there are several important ways need to say next. The code can be seen from the above enumeration Enum class will inherit our definition of this class.

public abstract class Enum>
        implements Comparable, Serializable {
    //枚举的名字,我们可以通valueof(name)来获取枚举值
    //这个就是指代我们定义的枚举实例的名字,比如上面的“SPRING”和“SUMMER”等
    private final String name;

    public final String name() {
        return name;
    }
    //枚举的大小,枚举值比较大小默认的就是比较这个值的大小
    //这个值的大小是根据我们定义枚举值的顺序来的,比如一个
    //枚举值我们第一个定义那么这个枚举的ordinal就是0,比如上面定义的“SPRING”的ordinal值就是0
    // 上面定义的“SUMMER”的值就是1,还有一点需要说明的就是:当我们在switch中使用枚举类型时,
    //编译后就是匹配的这个ordinal值
    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;
    }

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

    public final int hashCode() {
        return super.hashCode();
    }
    //禁止克隆
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
    //比较ordinal的大小
    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;
    }

    @SuppressWarnings("unchecked")
    public final Class getDeclaringClass() {
        Class clazz = getClass();
        Class zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;
    }
    //通过name来获得枚举
    public static > T valueOf(Class 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");
    }
}

3. The sequence of enumeration class implemented

All previous singleton has a bigger problem is that once realized Serializable interface, it is no longer a single case, because each call to readObject () method returns is a newly created object out there is a workaround is to use readResolve () method to avoid this to happen. However, in order to ensure the enumeration type as Java Specification said, enumeration each enumeration type definition extremely unique in the serialization and de-serialization of enumerated types, Java is done in the JVM a special provision that Java is only the name attribute to output the results enumerated objects in the sequence of the time, when deserialization is to enumerate the object based on name lookup by valueOf method of java.lang.Enum. Meanwhile, the compiler does not allow any customization of this serialization mechanism, thus disabling the writeObject, readObject, readObjectNoData, writeReplace readResolve and other methods.

    //Quarter是一个枚举类
    Quarter[] values = Quarter.values();
    Quarter one = values[0];
    
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\enum.txt"));
    objectOutputStream.writeObject(one);
    objectOutputStream.close();
    
    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\enum.txt"));
    Object object = objectInputStream.readObject();
    //返回true
    System.out.println(object==one);

The enumeration class in the sequence of the time will only name Zodiac serialized. For first determines the type of deserialized class readObject deserializing method in ObjectInputStream class code above, if the type is an enumeration class to obtain the re-enumeration class method calls the valueof.

    public static > T valueOf(Class 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);
    }

As can be seen by the above code, valueof method according cached obtained when a class or class enumeration class initialization. So there will be only a system or enumeration class.

4. enumeration achieve single

The single common implementations have a big problem is that if the separate class to serialize and then deserialize then there will be two separate classes with a jvm. This problem does not occur by deserializing the above enumeration class analysis, so to achieve a single embodiment mode enumeration class is a good choice.

public enum  EnumSingleton {
  INSTANCE;
  public EnumSingleton getInstance(){
      return INSTANCE;
  }
}

5. The process of creating enumerate instances are thread-safe

We can see from the second portion of code to create enumerate instances are created in a static block. Static code block will be executed during initialization class, and class initialization process is thread-safe, so the process of creating enumerate instances are thread-safe.

Leave a Reply