簡單看看java之枚舉


  枚舉類這個類用的比較少,對這個不怎么熟悉,最近看源碼剛好可以好好了解一下,那么,枚舉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類了),可以有自己的屬性,方法,也可以覆蓋父類的方法(可以自己試試),就跟平常類一樣的使用!就是寫法略怪,習慣就好!!!

  話說昨天下午弄了好久,終於被我找到在博客園配置出了血小板看板娘了,哈哈哈,我現在聲明一句:血小板就是我老婆,不接受反駁!嘿嘿嘿@_@


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM