關於枚舉

枚舉可以序列化嗎?

答:能。枚舉雖然能被序列化,但是實現與Class的序列化不同。oracle官方文檔中有寫:

1.12 Serialization of Enum Constants
Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant’s name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant’s enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream.
The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored–all enum types have a fixed serialVersionUID of 0L. Documenting serializable fields and data for enum types is unnecessary, since there is no variation in the type of data sent.

意思就是,枚舉的序列化,僅僅將枚舉對象的name輸出到結果中,反序列化的時候是通過java.lang.Enum的valueOf()方法,根據name來查找內存中的枚舉對象。
同時,編譯器不允許任何對這種序列化機制的定製,所以禁用了枚舉的writeObject, readObject, readObjectNoData, writeReplace, readResolve方法。

枚舉可以被繼承嗎?

通過反編譯枚舉類的class文件,我們會發現enum會被javac編譯成public final class T extends Enum定義,並且枚舉項是通過static塊來初始化的。

//源代碼
public enum T {
    SPRING,SUMMER,AUTUMN,WINTER;
}
//反編譯後
public final class T extends Enum
{
    //省略部分內容
    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
        });
    }
}

final修飾的類不能被繼承,所以,枚舉不能被繼承。

爲什麼推薦用枚舉實現單例?

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
} 

用枚舉實現單例有如下優勢:

  1. 簡單;

  2. 可以保證安全問題。如何保證?
    由上面反編譯的代碼可知,枚舉類是通過靜態塊初始化的。
    static靜態資源是在類第一次被用到的時候加載並初始化的,而類加載、初始化兩個動作都是線程安全的(因爲JVM在加載枚舉類的時候,會使用ClassLoader的loadClass方法,這個方法用同步代碼塊保證了線程安全),所以創建枚舉是線程安全的。

  3. 可以保證不被反序列化破壞單例。如何保證?
    由第一個問題“枚舉可以序列化嗎?”可知,枚舉的反序列化特殊,是不會創建新對象的。

  4. 可以保證不被反射破壞單例。如何保證?
    默認的構造方法爲 private 修飾的。如果添加構造方法同樣必須使用 private 來修飾。並且在 Jvm 層面保證了 構造方法不能被反射取到或者使用。在編譯階段,編譯器會爲我們自己定義的構造方法添加 name 和 ordinal 兩個參數。在這裏插入圖片描述

枚舉類何時被jvm加載?

答:由上一個問題可知,枚舉類實際上是用靜態塊初始化的,所以,枚舉類在第一次被使用到的時候加載並初始化。

發佈了37 篇原創文章 · 獲贊 12 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章