?初始化和清理涉及到程序的安全性 ? 問題往往出現(xiàn)在 忘記初始化 以及 忘記回收資源
5.1 用構(gòu)造確保初始化 ?繼承自c++ 的方式
構(gòu)造方法是為了 在保證我們初始化對象 ?當(dāng)我們調(diào)用對象時 他已經(jīng)存在
創(chuàng)建對象時,如果類具有構(gòu)造器,java就會在用戶有能力調(diào)用對象之前自動調(diào)用相應(yīng)的構(gòu)造器,保證初始化的進(jìn)行
我們使用對象之前保證對象已經(jīng)創(chuàng)建好
class ?Rock{
? ? ? ? ? ? ? ?public Rock(){ ?// 這就是個典型的構(gòu)造方法 ?方法名與類名相同 ?同時不需要任何返回類型 ? 方法首字母不為小寫
? ? ? ? ? ? ? ? }
}
創(chuàng)建對象 ? new Rock(); ?
默認(rèn)構(gòu)造器:不接受參數(shù)的構(gòu)造器叫做默認(rèn)構(gòu)造器?
構(gòu)造器也可帶參數(shù),用來指定如何創(chuàng)建對象
class Tree{
? ? ? ?public Tree(int ?height){
? ? ? ? }
}
new Tree(12);
上面 Tree(int ) 是Tree唯一的構(gòu)造方法 ?所以編譯器不會允許你通過其他方式來構(gòu)建對象,必須使用 Tree(int)
默認(rèn)情況下 我們創(chuàng)建一個類 沒有指定構(gòu)造方法 java會默認(rèn) 該類具有 默認(rèn)構(gòu)造器 ?以用來創(chuàng)建對象,但一旦指定就不能按照默認(rèn)構(gòu)造器來初始化對象 只能按指定的來構(gòu)建對象
5.2方法重載:方法名相同而形式參數(shù)不同 ? ??
public void test();?
public void test (int value);
如何區(qū)分重載方法 1. 方法名相同 ?2. 參數(shù)類型列表不同(參數(shù)不同) ?
這里注意 和返回類型無關(guān) ?理論上如果返回類型都并不一樣 不應(yīng)算作重載 ?如果 方法名相同 返回值不同 編譯器會提示錯誤
為什么會這樣 ?
public int ?test();
public void test();
當(dāng)我們調(diào)用 ?int k = test(); 時 ?似乎編譯器是可以識別應(yīng)該調(diào)用那個方法要
但是當(dāng)我們并不在乎返回值時 ? 直接調(diào)用 test(); 編譯器就徹底無法區(qū)分要調(diào)用那個方法?
所以 返回類型的不同 不能區(qū)分方法重載
5.3 默認(rèn)構(gòu)造器
如果你的類中沒有構(gòu)造器 則編譯器會自動幫你創(chuàng)建一個默認(rèn)構(gòu)造器
class Bird{
? ? ? public void fly(){
? ? ? }
}
new Bird() ?我們可以利用編譯器默認(rèn)提供的 默認(rèn)構(gòu)造器來構(gòu)造對象 ?如果你指定的構(gòu)造器 則編譯器不會自動創(chuàng)建默認(rèn)構(gòu)造器
class ?Bird{
? ?public Bird(int ?age){
? ?}
}
new Bird () ?// 這樣編譯器會提示錯誤 ?沒有匹配到合適的構(gòu)造器
new Bird(1)// 這樣才是正確的姿勢
5.4 this 關(guān)鍵字
public class BananaPeel{
? ? ? public void peel(int l){
? ? ? }
}
public class DoPeel{
? ? ? ? ? ?public static mian(String[] args){
? ? ? ? ? ? ? ? ? ? BananaPeel a = new BananaPeel();
? ? ? ? ? ? ? ? ? ? BananaPeel b = new BananaPeel();
? ? ? ? ? ? ? ? ? ? a.peel(1);
? ? ? ? ? ? ? ? ? ? ?b.peel(2);
? ? ? ? ? ? ? ?}
}
作者有兩個提問:1. 對于我們想指定對象發(fā)送消息調(diào)用方法 編譯器是如何知道調(diào)用那個對象的方法
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.如果我們只是用 a調(diào)用 peel方法 ?他是如何知道是a 還是 b 調(diào)用
引出:面向?qū)ο蟮恼Z法采用 發(fā)送消息給對象 ?編譯器在這里做了一些幕后工作 ? ?
它暗自把“所有操作對象的引用” 作為第一個參數(shù)傳遞給peel()。所以上述的方法調(diào)用變成了
Banana.peel(a,1);
Banana.peel(b,2);
由于引用是由編譯器偷偷傳遞的,所以我們沒有可使用的引用標(biāo)識符?
但是為此專門給我們提供了 關(guān)鍵字 this
this關(guān)鍵字只能在方法內(nèi)部使用,表示對“調(diào)用方法的那個對象”的引用。
clas 內(nèi)的方法調(diào)用
class ?Bird{
? ? ? public void fly(){
? ? ? }
? ? ? ?public ?void growUp(){
? ? ? ? ? ? ? ?this.fly(); ? // 通過this繼續(xù)調(diào)用 對象的其他方法?
? ? ? ? ? ? ? ? fly(); ? ? ? ?//上下兩種方式都是正確的 ?事實上 添加this 更加準(zhǔn)確,但是編譯器會自動添加 無需我們再次添加
? ? ? ? ?}?
}
從向?qū)ο蟀l(fā)送消息 到 對象內(nèi)部調(diào)用自身方法 我們可以看到 編譯器在默默的幫我們傳遞對象引用...
所以我們一般情況下是不需要使用this關(guān)鍵字,一下情況除外:
class Data{
private String data;
? ? public Date getData(String data){
? ? ? ? ? this.data = data; ? ? ? ? ?//方法參數(shù)名與 對象書屬性沖突
? ? ? ? ? ?return this; ? ? ? ? ? ? ? ? ? //需要返回 對象引用的方法
? ? ? }
}
5.4.1 在構(gòu)建器中調(diào)用構(gòu)造器

5.4.2 static的含義
static方法就是沒有this的方法,不能使用this 因為他不是屬于對象的 是屬于類的。?
1.在static方法內(nèi)部不能調(diào)用非靜態(tài)方法(除非通過參數(shù)傳遞引用調(diào)用或者方法中new處對象),非靜態(tài)成員變量也無法使用(除非作為參數(shù)傳遞進(jìn)方法中),反過來可以?
2.可以不創(chuàng)建對象的情況下 通過 類.方法名 來直接調(diào)用方法(權(quán)限允許的化)
static 修飾的方法具有全局方法的作用 修飾的變量 具有全局變量的作用
5.5 清理:終結(jié)處理和垃圾回收
finalize() java允許類中定義一個finalize()方法, 他的工作原理大概如下:
垃圾回收器準(zhǔn)備好釋放對象占用的存儲空間,將首先調(diào)用finalize()方法 并且在下一次垃圾回收動作發(fā)生時,才會真正回收對象占用的內(nèi)存。所以要是在垃圾回收時我們可以依靠finalize()做一些事情
垃圾回收也是會消耗資源的
1.對象也可能不被垃圾回收 (如果資源充足)
2.java中的垃圾回收不同與c++中的析構(gòu)
5.5.1 finalize()的用途何在
3.垃圾回收只有內(nèi)存有關(guān) ??
finalize 某種程度上實現(xiàn)了對象的內(nèi)存資源重新分配(資源的釋放,我們都知道java中對象資源的持久性) 但是我們知道java中一切都是對象,那么finalize如何進(jìn)行的資源分配呢 顯然通過java的方式是難以實現(xiàn)的?
finalize()在分配資源的時候采用了c語言的做法,而非java的做法,通過本地方法的調(diào)用 即java調(diào)用非java代碼實現(xiàn),本地方法目前只支持c c++ , 也就是說 finalize()通過調(diào)用c或者c++代碼來對 對象的資源進(jìn)行釋放...比如c的malloc()來分配內(nèi)存 調(diào)用free()來釋放內(nèi)存
從這里我們看出 finalize()是一種終極釋放資源的方式...所以他也是危險的 因為他帶來了未知性 c來釋放資源的確很快捷方便 但是對于java 來說也許其他地方也在使用該資源 只是我們沒有察覺到,然而這樣的釋放 將帶來巨大的問題,不在java規(guī)則中的釋放...帶來未知不可預(yù)判的問題
所以這不是一種普遍的處理工作 比較危險
5.5.2 你必須實施清理
需要了解到 java不允許創(chuàng)建局部對象 必須使用new 創(chuàng)建對象。?
那么想要釋放存儲空間 我們就要明白某個java中的方法
這里需要注意無論是 垃圾回收還是終結(jié)(finalize()) 都不保證一定發(fā)生 如果java虛擬機并未面臨內(nèi)存耗盡的情況 執(zhí)行垃圾回收就是一種資源浪費
5.5.3 終結(jié)的條件
從寫類的finalize() 方法,在方法中對對象的狀態(tài)進(jìn)行判斷,如果對象處于非正常狀態(tài),就拋出異常或者進(jìn)行警告提示,這么做當(dāng)對象的資源被釋垃圾回收時,會調(diào)用finalize()方法,從而判斷對象是否是正常狀態(tài),如果非正常這說明之前對對象的操作有問題,這里的finalize()的目的不是用來銷毀對象,而是判斷對象是否異常....不建議 這么玩 需要謹(jǐn)慎判斷對象是否真的需要finalize()清理
比如 ?一本書在必須由管理員檢查后后處理于正常狀態(tài) ?可以這么檢測對象是否異常
public class Book{
? ? ? ? private boolean cheackOut;
? ? ? ? public ?Book(boolean ?checkOut){ ? ?//book 初始化時設(shè)置為未被檢測過
? ? ? ? ? ? ? ? ?this.checkOut = cheackOut;
? ? ? ? ?}
? ? ? ? ? public void cheackBook(){ ? ? ? ? ? ? ?//book的檢測方法
? ? ? ? ? ? ? ?this.cheackOut ?= false;
? ? ? ? ? ? }
? ? ? ? ?@Override
? ? ? ? ? ?protected void finalize()throws Throwable { ? ?
? ? ? ? ? ? ? ? ? ?if(cheackOut){ ? ? ? ? ? ? ? ? ? ? ? ? ? // 如果沒有被檢測水過 則進(jìn)行日志提示 ?對象狀態(tài)異常 ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? Log.e(“錯誤”,"書籍沒有進(jìn)行檢測")
? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? super.finalize();
? ? ? ? ? ? }
}
main(Stirng arg[ ] ){
? ? ? ?Book novel = new Book(true); ? ?//new 處未被檢測的數(shù)據(jù)?
? ? ? ?nove.cheackBook(); ? ? ? ? ? ? ? ? ? //對書籍進(jìn)行檢測
? ? ? ?new Book(true); ? ? ? ? ? ? ? ? ? ? ? ?//new出對象 ?并且并沒有對象進(jìn)行檢測
? ? ? ?System.gc(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? //gc后就有可能能打印錯誤日志提示
}
這是檢測程序猿是否忘記 檢測書籍的一種方法 但是 是一種不一定會暴露出問題的方式
因為 finalize( )并不一定會回調(diào)
5.5.4 垃圾回收器如何工作
相當(dāng)復(fù)雜 有點暈 ...
java在堆上創(chuàng)建對象的速度快嗎(或者在堆上進(jìn)行空間的存儲速度釋放的速度快嗎)?
垃圾回收器對于提高對象的創(chuàng)建速度,有明顯的效果,某種程度上他使java的堆分配空間速度與堆棧的分配空間基本相同
為啥會這樣?
c++中堆像個院子,每個對象負(fù)責(zé)管理自己的底盤。散亂
java虛擬機中,堆的實現(xiàn)截然不同,他更像一個傳送帶,每分配一個新對象,他就往前移動一格。這得益于gc對堆空間的管理,使堆空間的對象排序整齊,同時對空間進(jìn)行回收,這樣堆指針就可以很容易移動到更靠近傳送帶的開始處,這樣java的堆指針簡單移動就可以分配新的空間,所以開辟新空間的速度很快
如何理解java中的垃圾回收機制?
引用計數(shù)器(常常被用來描述java的垃圾回收機制 事實上java虛擬機并不使用該技術(shù)實現(xiàn)GC):引用計數(shù)是一種簡單但是速度很慢的垃圾回收機制。 ?
實現(xiàn):每個對象都包含一個應(yīng)用計數(shù)器,當(dāng)引用連接至對象,引用計數(shù)器+1。當(dāng)引用離開作用域或者被置為null時,引用計數(shù)器-1,這個行為貫穿程序的整個生命周期,垃圾回收器會遍歷全部對象,當(dāng)發(fā)現(xiàn)引用計數(shù)器為0的對象,就釋放對象資源。缺陷當(dāng)對象互相引用時,由于應(yīng)用計數(shù)器無法置0所以無法釋放資源。 ?
java虛擬機使用機制(更快的模式):只找活的對象,從堆棧和靜態(tài)存儲區(qū)開始,遍歷所有引用,找到活著的對象,然后從對象,遍歷對象中的引用,繼續(xù)找,引用鏈可能會貫穿數(shù)個對象層,反復(fù)進(jìn)行,直到 根源于堆棧與靜態(tài)存儲區(qū)的引用 所形成的網(wǎng)絡(luò)全部被訪問到為止。這樣相互引用的對象將不會被發(fā)現(xiàn)(堆棧中沒有引用)也就被回收了
java虛擬機的自適應(yīng)技術(shù):是指java虛擬機根據(jù)內(nèi)存回收開銷以及垃圾數(shù)量來動態(tài)切換垃圾回收機制的方式。
找到活著的對象,如何處理取決于不同的java虛擬機實現(xiàn)機制。 停止復(fù)制是一種常用技術(shù),他并非后臺運行程序,他會停止程序的運行,將活著的對象從當(dāng)前堆復(fù)制到另一個堆上,并整齊的排列起來,原堆上留下的對象都是清理對象,將資源釋放。因為對象的復(fù)制,所以引用指向的地址也需要進(jìn)行改變。
?缺點1:維護(hù)兩個堆,堆內(nèi)存要求比原來高一倍。
解決:java虛擬機將堆內(nèi)存 分配幾塊較大的內(nèi)存區(qū)域,復(fù)制動作只發(fā)生在這幾塊區(qū)域上面
缺點2:但程序進(jìn)入穩(wěn)定期后,垃圾數(shù)量很少,甚至沒有,這時因為少量垃圾,復(fù)制大量對象簡直是一種浪費。
解決:java虛擬機會檢測 要是沒有新垃圾的產(chǎn)生,就會轉(zhuǎn)換到 標(biāo)記-清掃模式
標(biāo)記-清掃模式:?java虛擬機從堆棧與靜態(tài)存儲區(qū)的引用下手,遍歷所有引用,對對象進(jìn)行標(biāo)識,這個過程不回收對象,當(dāng)標(biāo)記工作完成后,清理為標(biāo)記的所有對象,這樣會造成堆內(nèi)存的不連續(xù)性,如果需要連續(xù)空間還是要進(jìn)行 停止復(fù)制來操作所有對象。 標(biāo)記清掃 也是必須暫停程序的
綜合:java虛擬機中 內(nèi)存分配以較大的塊為單位。如果對象較大,會單獨占用塊(不對其進(jìn)行復(fù)制操作,太消耗資源),對于其他較小的對象就行復(fù)制操作,gc時,將他們往廢棄的塊中拷貝,每個塊都有相應(yīng)的代數(shù)來記錄是否開存活,如果塊在某處被引用,代數(shù)會增加。java虛擬機會進(jìn)行監(jiān)控,如果對象很穩(wěn)定,垃圾回收效率很低時,則進(jìn)行標(biāo)記-清掃,如果產(chǎn)生碎片空間較多時,就會接換會停止-復(fù)制模式 來整理空間。 這就是自適應(yīng)技術(shù) ??
提速的附加技術(shù)(Just-in-time,JIT) 即時編譯器技術(shù)(有點難理解): 這是一種加載器操作的有關(guān)技術(shù),這種技術(shù)可以把程序全部或者部分翻譯成本地機器碼,程序運行速度因此的到提升。
實現(xiàn)機制:當(dāng)我們要創(chuàng)建一個對象,編譯器會先找到其 .class文件,然后將該類的字節(jié)碼轉(zhuǎn)入內(nèi)存。 這時有JIT兩種方案選擇:
1.讓即時編譯器編譯所有的代碼
缺陷:這種加載動作會算落在整個程序生命周期中,累積起來要花更多時間
并且會增加可執(zhí)行代碼的長度(字節(jié)碼長度要比即時編譯器展開后的本地機器碼小很多)導(dǎo)致頁面調(diào)度 降低程序速度
2.惰性評估?
即時編譯器只在必要時才編譯代碼,從不會執(zhí)行的代碼不會被jit編譯。新版JDK中的java HotSpot技術(shù)就采用了類似的方法,代碼每次被執(zhí)行都會做一些優(yōu)化,所以執(zhí)行次數(shù)越多,速度就越快
5.6 成員初始化
java盡力保證:所有變量在使用前的初始化 局部變量未初始化編譯時會以錯誤形式提示
在類中如果一個基本類型沒有賦值,java會自動默認(rèn)給他一個初始值 ? 但是一個類型引用沒有賦值 直接就給一個null
5.6.1 指定初始化
1.直接在定義成員變量的地方賦值 ? 對于非基本類型也適用
2.可以調(diào)用某個方法為成員變量賦值
5.7 構(gòu)造器初始化
可以用構(gòu)造器初始化 ?但是 我們必須明白 自動初始化的進(jìn)行在 構(gòu)造器被調(diào)用之前
public class Test{
? ? int i;
? ? ?public Test(){
? ? ? ? ? ? i = 9; ?//i先被賦值為 0 再被賦值為9
? ? ? ?}
}
對于所有基本類型 和 對象引用 包括自定義初值的變量 都是成立的 都是屬性先被賦值再 進(jìn)行方法賦值
5.7.1 初始化順序
先成員變量 ?后方法
public class House {
? private ? Window ?w1 = new Window(1);
? ? public House(){
? ? ? ?Window ?w2 = new Window(2);
? ? }?
? ?Window w3 = new Window(3);
? public void ?f(){
? ?}
? ?Window w4 = new Window(4);
? ? Window w5 = new Window(5);
}
main(String arg[ ]}{
new House()
}
Window的創(chuàng)建順序 ?: ? w1, w3 , w4 , w5 , w2
5.7.2 靜態(tài)數(shù)據(jù)初始化
無論創(chuàng)建多少個對象,靜態(tài)數(shù)據(jù)都只占一份存儲區(qū)域
static 關(guān)鍵字不能應(yīng)用于局部變量 只能作用于域

靜態(tài)初始化只在必要時進(jìn)行 ---》類創(chuàng)建時 初始化 ? 即代碼中出現(xiàn)Test類時
初始化順序 先域塊 后構(gòu)造 ?先初始靜態(tài)對象(成員變量) 后初始非靜態(tài)對象(成員變量)
總結(jié) 一下對象的創(chuàng)建過程 ?假設(shè)有個名為Dog的類(每個對象創(chuàng)建都會將成員變量進(jìn)行初始化)
1.即使沒有顯示使用static 構(gòu)造器實際上也是靜態(tài)方法。 當(dāng)首次創(chuàng)建類型為Dog的對象時,或者Dog類的靜態(tài)方法/靜態(tài)域首次被訪問時,java解釋器必須查找類的路徑,已定位Dog.class類
2.然后載入Dog.class(創(chuàng)建一個class對象),有關(guān)靜態(tài)初始化的所有動作都會執(zhí)行。因此 靜態(tài)初始化是在Class對象首次加載的時候進(jìn)行一次
3.當(dāng)用 new Dog()創(chuàng)建對象時,首先在堆上為Dog對象分配存儲空間
4.這塊存儲空間會被清零 這就自動將所有Dog對象中的基本類型數(shù)據(jù)設(shè)置成了默認(rèn)值 ?引用設(shè)置為null
5.執(zhí)行所有出現(xiàn)于字段定義處的初始化動作
6.執(zhí)行構(gòu)造器?
5.7.3 顯示的靜態(tài)初始化
java中的靜態(tài)初始化動作組織成一個特殊的“靜態(tài)子句” 叫做靜態(tài)塊
public class Spoon{
? ? ? ?static int ?i;
? ? ? ? static{
? ? ? ? ? ? ?i = 47;
? ? ? ? ?}?
}
靜態(tài)塊也在 類被加載成class 對象時 進(jìn)行初始化
5..4 非靜態(tài)實例初始化
java中也有被稱為實例初始化的類似語法,用來初始化每個非靜態(tài)成員變量
public class Spoon{
? ? ? ?public int ?i;
? ? ? ?public ?Object obj;
? ? ? ?{
? ? ? ? ? ? ?i = 47;
? ? ? ? ? ? ?obj = new Object();
? ? ? ? ? }?
}
這種初始化在對象創(chuàng)建時進(jìn)行 (new 得到對象時) 在構(gòu)造方法之前執(zhí)行 ?每個對象都會進(jìn)行各自的初始化?
5.8數(shù)組初始化 ?(可以將數(shù)組認(rèn)為是對象)
數(shù)組是相同類型的 用一個標(biāo)識符名稱封裝到一起的一個對象序列或基本類型數(shù)據(jù)序列
第一數(shù)組定義 : ? 類型 [ ] ?標(biāo)識符名稱 ? ? ?int [ ] top
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?類型 ? ?標(biāo)識符名稱 [ ?] ? ?int ?top [ ] 都行
int [] a = {1,2,3,4,5};
int [] b= a;
for(int i = 0,i<b.length,i++){
? ? b[ i ] ?= ?b[ i ] +1 ;
}
for(int i = 0,i<a.length,i++){
? ? ? ? System.out.println(“a [”+i+"] ="+a[ i ]);
}
//打印結(jié)果為 a[0] =2 ? ?a[1] =3 ? ?a[2] =4 ? ?a[3] =5 ? ?a[4] =6? ? ?
我們發(fā)現(xiàn) 數(shù)組可以理解為對象 ?= 進(jìn)行的是引用傳遞 ?
數(shù)組的兩種初始化形式
int【】 a ?= {1,2,3,4}
int 【】a = new int【】{1,2,3,4};這種形式也是可以的
int 【】a = new int【10】;是代表 數(shù)組長度 即空間大小 ?這種情況下其實只是開辟了空間但并沒有初始化?
對于基本類型數(shù)組 new方式創(chuàng)建的數(shù)組 ?對應(yīng)位置上會進(jìn)行 值的初始化 比如 ?int【0】=0 ?int【1】=0....int【9】=0
boolean 默認(rèn)為flase
數(shù)組是動態(tài)創(chuàng)建的
Arrays.toString(a) ? 方法可以將以為數(shù)據(jù)轉(zhuǎn)化成 ?[1,2,3,4]的字符串 方便我們打印
如果我們創(chuàng)建了一個 非基本類型的數(shù)組 ? 那我們就創(chuàng)建了一個引用數(shù)組 ?
我們直到 進(jìn)行 a[i] = new ?Integer(i); ?把對象賦值給引用 初始化才算結(jié)束
兩種初始化的好劣
int【】 a ?= {1,2,3,4} ? ? ? ? ? ?//這種方式很簡單 但是只能用在初始化的位置 ? 不能當(dāng)參數(shù)傳遞
int 【】a = new int【】{1,2,3,4};這種形式也是可以的 ?// 這種方式就比較靈活啦
public void doArray( String [ ] ?data ?){
}
doArray(new String[]{"K","L","P"}); ?// 靈活
5.8.1 可變參數(shù)列表
所有對象都繼承自 Object ?那么 我們?nèi)绻褂?Object[] 數(shù)組 就可以填加任意的數(shù)據(jù)類型 ?
new Object【】{“sdf”,1,new Object() ,} 截尾的逗號可要可不要
可變參數(shù)要求: ?類型... 參數(shù)名稱
public void doTest(String ?f , Object...arg) ? 不定參數(shù)必須放在最后?
事實上 不定參數(shù)就是一個數(shù)組 ?當(dāng)我們指定參數(shù)時,編譯器會自動為我們填充
doTest(“123”,new Object(),new Object(),new Object)例如這樣
arg【0】 = new Object();
arg【1】= new Object();
arg【2】= new Object();
5.9 枚舉類型
enum關(guān)鍵字 ?
public enum Spiciness{
? ? ? NOT,MILD,MEDIUM,HOT,F(xiàn)LAMING
}
這樣就創(chuàng)建了 Spiciness 的枚舉類型,它具有5個名值。 由于枚舉類型的實例是常量,因此按照命名慣例他們都用大寫字母表示