[转载] java 枚举Enum源码解析
參考鏈接: 使用Java枚舉
應(yīng)用場(chǎng)景?
枚舉是單例模式中的一種。面試官系統(tǒng)之設(shè)計(jì)模式(單例模式)?
簡(jiǎn)單來(lái)講就是只能實(shí)例化一次,線程安全且性能高。枚舉通常用來(lái)列舉一個(gè)類(lèi)型的有限實(shí)例集合,我們可以使用常量集來(lái)實(shí)現(xiàn),jdk1.5添加了枚舉(enum)支持,解決了常量集的一些缺陷?
常量集中的變量不會(huì)必然在指定的范圍內(nèi)常量能夠提供的功能很少,難于使用常量意義不明確,沒(méi)有名字修改或增加枚舉值后需要修改的代碼多,不便于維護(hù)
關(guān)鍵字enum可以將一組具名的值的有限集合創(chuàng)建為一種新的類(lèi)型,而這些具名的值可以作為常規(guī)的組件使用。?
??
一、定義?
//Enum類(lèi)是java.lang包中一個(gè)類(lèi),他是Java語(yǔ)言中所有枚舉類(lèi)型的公共基類(lèi)。
public abstract class Enum<E extends java.lang.Enum<E>> implements Comparable<E>, Serializable?
1.抽象類(lèi)。?
首先,抽象類(lèi)不能被實(shí)例化,所以我們?cè)趈ava程序中不能使用new關(guān)鍵字來(lái)聲明一個(gè)Enum,所以枚舉一旦創(chuàng)建就只有一個(gè)實(shí)例。?
如果想要定義可以使用這樣的語(yǔ)法:?
enum enumName{
? ? value1,value2
? ? method1(){}
? ? method2(){}
}?
其次,看到抽象類(lèi),第一印象是肯定有類(lèi)繼承他。至少我們應(yīng)該是可以繼承他的,所以:?
public class testEnum extends Enum{
}
public class testEnum extends Enum<Enum<E>>{
}
public class testEnum<E> extends Enum<Enum<E>>{
}?
嘗試了以上三種方式之后,得出以下結(jié)論:Enum類(lèi)無(wú)法被繼承。?
為什么一個(gè)抽象類(lèi)不讓繼承?enum定義的枚舉是怎么來(lái)的?難道不是對(duì)Enum的一種繼承嗎?帶著這些疑問(wèn)我們來(lái)反編譯以下代碼:?
? enum Color {RED, BLUE, GREEN}?
編譯器將會(huì)把他轉(zhuǎn)成如下內(nèi)容:?
public final class Color extends Enum<Color> {
? public static final Color[] values() { return (Color[])$VALUES.clone(); }
? public static Color valueOf(String name) { ... }
?
? private Color(String s, int i) { super(s, i); }
?
? public static final Color RED;
? public static final Color BLUE;
? public static final Color GREEN;
?
? private static final Color $VALUES[];
?
? static {
? ? RED = new Color("RED", 0);
? ? BLUE = new Color("BLUE", 1);
? ? GREEN = new Color("GREEN", 2);
? ? $VALUES = (new Color[] { RED, BLUE, GREEN });
? }
}??
我們看到當(dāng)我們定義一個(gè)枚舉,編譯器其實(shí)是為我們創(chuàng)建了一個(gè)繼承自Emum的類(lèi)?
??
枚舉實(shí)例對(duì)應(yīng)新類(lèi)中的static final 變量該類(lèi)為 final類(lèi)型,不可被繼承增加了兩個(gè)方法??
? valueOf(String) 從String構(gòu)造枚舉類(lèi)型values() 返回一個(gè)由枚舉對(duì)象構(gòu)成的數(shù)組添加了一個(gè)靜態(tài)初始化器 static{},用來(lái)初始化枚舉實(shí)例,和枚舉實(shí)例數(shù)組,也就是 values()返回?cái)?shù)組
PS:由于JVM類(lèi)初始化是線程安全的,所以可以采用枚舉類(lèi)實(shí)現(xiàn)一個(gè)線程安全的單例模式。?
??
2.實(shí)現(xiàn)Comparable和Serializable接口。?
??
Enum實(shí)現(xiàn)了Serializable接口,可以序列化。 Enum實(shí)現(xiàn)了Comparable接口,可以進(jìn)行比較,默認(rèn)情況下,只有同類(lèi)型的enum才進(jìn)行比較(原因見(jiàn)后文),要實(shí)現(xiàn)不同類(lèi)型的enum之間的比較,只能復(fù)寫(xiě)compareTo方法。?
3.泛型:**<E extends Enum<E>>**?
怎么理解<E extends Enum<E>>??
?
?首先,這樣寫(xiě)只是為了讓Java的API更有彈性,他主要是限定形態(tài)參數(shù)實(shí)例化的對(duì)象,故要求只能是Enum,這樣才能對(duì) compareTo 之類(lèi)的方法所傳入的參數(shù)進(jìn)行形態(tài)檢查。所以,我們完全可以不必去關(guān)心他為什么這么設(shè)計(jì)。?
?
這里倒是可以關(guān)注一下泛型中extends的用法,以及K V O T E ? object這幾個(gè)符號(hào)之間的區(qū)別。?
好啦,我們回到這個(gè)令人實(shí)在是無(wú)法理解的<E extends Enum<E>>?
首先我們先來(lái)“翻譯”一下這個(gè)Enum<E extends Enum<E>>到底什么意思,然后再來(lái)解釋為什么Java要這么用。 我們先看一個(gè)比較常見(jiàn)的泛型:List<String>。這個(gè)泛型的意思是,List中存的都是String類(lèi)型,告訴編譯器要接受String類(lèi)型,并且從List中取出內(nèi)容的時(shí)候也自動(dòng)幫我們轉(zhuǎn)成String類(lèi)型。 所以Enum<E extends Enum<E>>可以暫時(shí)理解為Enum里面的內(nèi)容都是E extends Enum<E>類(lèi)型。 這里的E我們就理解為枚舉,extends表示上界,比如: List<? extends Object>,List中的內(nèi)容可以是Object或者擴(kuò)展自O(shè)bject的類(lèi)。這就是extends的含義。 所以,E extends Enum<E>表示為一個(gè)繼承了Enum<E>類(lèi)型的枚舉類(lèi)型。 那么,Enum<E extends Enum<E>>就不難理解了,就是一個(gè)Enum只接受一個(gè)Enum或者他的子類(lèi)作為參數(shù)。相當(dāng)于把一個(gè)子類(lèi)或者自己當(dāng)成參數(shù),傳入到自身,引起一些特別的語(yǔ)法效果。?
為什么Java要這樣定義Enum?
首先我們來(lái)科普一下enum,?
enum Color{
? ? RED,GREEN,YELLOW
}
enum Season{
? ? SPRING,SUMMER,WINTER
}
public class EnumTest{
? ? public static void main(String[] args) {
? ? ? ? System.out.println(Color.RED.ordinal());
? ? ? ? System.out.println(Season.SPRING.ordinal());
? ? }
}?
代碼中兩處輸出內(nèi)容都是 0 ,因?yàn)槊杜e類(lèi)型的默認(rèn)的序號(hào)都是從零開(kāi)始的。?
要理解這個(gè)問(wèn)題,首先我們來(lái)看一個(gè)Enum類(lèi)中的方法(暫時(shí)忽略其他成員變量和方法):?
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
? ? private final int ordinal;
?
? ? 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;
? ? }
}?
首先我們認(rèn)為Enum的定義中沒(méi)有使用Enum<E extends Enum<E>>,那么compareTo方法就要這樣定義(因?yàn)闆](méi)有使用泛型,所以就要使用Object,這也是Java中很多方法常用的方式):?
public final int compareTo(Object o)??
當(dāng)我們調(diào)用compareTo方法的時(shí)候依然傳入兩個(gè)枚舉類(lèi)型,在compareTo方法的實(shí)現(xiàn)中,比較兩個(gè)枚舉的過(guò)程是先將參數(shù)轉(zhuǎn)化成Enum類(lèi)型,然后再比較他們的序號(hào)是否相等。那么我們這樣比較:?
Color.RED.compareTo(Color.RED);
Color.RED.compareTo(Season.SPRING);?
如果在compareTo方法中不做任何處理的話(huà),那么以上這段代碼返回內(nèi)容將都是true(因?yàn)镾eason.SPRING的序號(hào)和Color.RED的序號(hào)都是 0 )。但是,很明顯, Color.RED和Season.SPRING并不相等。?
但是Java使用Enum<E extends Enum<E>>聲明Enum,并且在compareTo的中使用E作為參數(shù)來(lái)避免了這種問(wèn)題。 以上兩個(gè)條件限制Color.RED只能和Color定義出來(lái)的枚舉進(jìn)行比較,當(dāng)我們?cè)噲D使用Color.RED.compareTo(Season.SPRING);這樣的代碼是,會(huì)報(bào)出這樣的錯(cuò)誤:?
The method compareTo(Color) in the type Enum<Color> is not applicable for the arguments (Season)?
他說(shuō)明,compareTo方法只接受Enum<Color>類(lèi)型。?
Java為了限定形態(tài)參數(shù)實(shí)例化的對(duì)象,故要求只能是Enum,這樣才能對(duì) compareTo之類(lèi)的方法所傳入的參數(shù)進(jìn)行形態(tài)檢查。 因?yàn)椤凹t色”只有和“綠色”比較才有意義,用“紅色”和“春天”比較毫無(wú)意義,所以,Java用這種方式一勞永逸的保證像compareTo這樣的方法可以正常的使用而不用考慮類(lèi)型。?
?
?PS:在Java中,其實(shí)也可以實(shí)現(xiàn)“紅色”和“春天”比較,因?yàn)镋num實(shí)現(xiàn)了Comparable接口,可以重寫(xiě)compareTo方法來(lái)實(shí)現(xiàn)不同的enum之間的比較。?
?
二、成員變量?
在Enum中,有兩個(gè)成員變量,一個(gè)是名字(name),一個(gè)是序號(hào)(ordinal)。 序號(hào)是一個(gè)枚舉常量,表示在枚舉中的位置,從0開(kāi)始,依次遞增。?
/**
?* @author hollis
?*/
private final String name;
public final String name() {
? ? return name;
}
private final int ordinal;
public final int ordinal() {
? ? return ordinal;
}?
三、構(gòu)造函數(shù)?
前面我們說(shuō)過(guò),Enum是一個(gè)抽象類(lèi),不能被實(shí)例化,但是他也有構(gòu)造函數(shù),從前面我們反編譯出來(lái)的代碼中,我們也發(fā)現(xiàn)了Enum的構(gòu)造函數(shù),在Enum中只有一個(gè)保護(hù)類(lèi)型的構(gòu)造函數(shù):?
protected Enum(String name, int ordinal) {
? ? this.name = name;
? ? this.ordinal = ordinal;
}?
文章開(kāi)頭反編譯的代碼中private Color(String s, int i) { super(s, i); }中的super(s, i);就是調(diào)用Enum中的這個(gè)保護(hù)類(lèi)型的構(gòu)造函數(shù)來(lái)初始化name和ordinal。?
四、方法?
序列化?
禁止了基礎(chǔ)的序列化方法,調(diào)用readObject()和writeObject()時(shí)拋出異常
? ? 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");
? ? }?
??
??
??
參照:?
(1)https://blog.csdn.net/stubbornant/article/details/51480727?
(2)http://www.hollischuang.com/archives/92
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的[转载] java 枚举Enum源码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .net 怎么循环得到数组里的值_Has
- 下一篇: python django flask介