《Java核心技術 卷Ⅰ》 第5章 繼承前端
關鍵字extend
表示繼承。java
public class Manager extends Employee { // 添加方法和域 }
extend
代表正在構造的新類派生於一個已存在的類。git
已存在的類稱爲超類(superclass)、基類(base class)或父類(parent class);
新類稱爲子類(subclass)、派生類(derived class)或孩子類(child class)。程序員
子類有超類沒有的功能,子類封裝了更多的數據,擁有更多的功能。github
因此在擴展超類定義子類時,僅須要指出子類與超類的不一樣之處。數組
有時候,超類的有些方法並不必定適用於子類,爲此要提供一個新的方法來覆蓋(override)超類中的這個方法:安全
public class Manager extends Employee { private double bonus; ... public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } ... }
這裏因爲Manager
類的getSalary
方法並不能直接訪問超類的私有域。app
這是由於儘管子類擁有超類的全部域,可是子類無法直接獲取到超類的私有部分,由於超類的私有部分只有超類本身才可以訪問。而子類想要獲取到私有域的內容,只能經過超類共有的接口。ide
而這裏Employee
的公有方法getSalary
正是這樣的一個接口,而且在調用超類方法使,要使用super
關鍵字。工具
那你可能會好奇:
super
關鍵字不行麼?Employee
類的getSalary
方法不該該是被Manager
類所繼承了麼?這裏若是不使用super
關鍵字,那麼在getSalary
方法中調用一個getSalary
方法,勢必會引發無限次的調用本身。
關於super
和this
須要注意的是:他們並不相似,由於super
不是一個對象的引用,不能將super
賦給另外一個對象變量,它只是一個指示編譯器調用超類方法的特殊關鍵字。
public Manager(String name, double salary, int year, int month, int day) { super(name, salary, year, month, day); bonus = 0; }
語句super(...)
是調用超類中含有對應參數的構造器。
Q1:爲何要這麼作?
A1:因爲子類的構造器不能訪問超類的私有域,因此必須利用超類的構造器對這部分私有域進行初始化。可是注意,使用super
調用構造器的語句必須是子類構造器的第一條語句。
Q2:必定要使用麼?
A2:若是子類的構造器沒有顯示地調用超類構造器,則將自動地調用超類默認(沒有參數)的構造器;若是超類並無不帶參數構造器,而且子類構造器中也沒有顯示調用,則Java編譯器將報告錯誤。
Employee[] staff = new Employee[3]; staff[0] = manager; staff[1] = new Employee(...); staff[2] = new Employee(...); for(Employee e : staff) { System.out.println(e.getName() + "" + e.getSalary()); }
這裏將e
聲明爲Emplyee
對象,可是實際上e
既能夠引用Employee
對象,也能夠引用Manager
對象。
Employee
對象時,e.getSalary()
調用的是Employee
的getSalary
方法Manager
對象時,e.getSalary()
調用的是Manager
的getSalary
方法虛擬機知道e
實際引用的對象類型,因此可以正確地調用相應的方法。
一個對象變量能夠指示多種實際類型的現象被稱爲多態(polymorphism)。在運行時可以自動地選擇調用哪一個方法的現象稱爲自動綁定(dynamic binding)。
集成並不只限於一個層次。
由一個公共超類派生出來的全部類的集合被稱爲繼承層次(inheritance hierarchy),在繼承層次中,從某個特定的類到其祖先的路徑被稱爲該類的繼承鏈(inheritance chain)。
Java不支持多繼承,有關Java中多繼承功能的實現方式,見下一章有關接口的部分。
"is-a"
規則的另外一種表述法是置換法則,它代表程序中出現超類對象的任何地方均可以用子類對象置換。
Employee e; e = new Employee(...); e = new Manager(...);
在Java程序設計語言中,對象變量是多態的。
Manager boss = new Manager(...); Employee[] staff = new Employee[3]; staff[0] = boss;
這個例子中,雖然staff[0]
與boss
引用同一個對象,可是編譯器將staff[0]
當作Employee
對象,這意味着這樣調用是沒有問題的:
boss.setBonus(5000); // OK
可是不能這樣調用:
staff[0].setBonus(5000); // Error
這裏由於staff[0]
聲明的類型是Employee
,而setBonus
不是Employee
類的方法。
儘管把子類賦值給超類引用變量是沒有問題的,但這並不意味着反過來也能夠:
Manager m = staff[2]; // Error
若是這樣賦值成功了,那麼編譯器將m
當作是一個Manager
類,在調用setBonus
因爲所引用的Employee
類並無該方法,從而會發生運行時錯誤。
弄清楚如何在對象上應用方法調用很是重要。
好比有一個C
類對象x
,C
有一個方法f(args)
。
如今以調用x.f(args)
爲例,說明調用過程的詳細描述:
C
類中可能有多個同名的方法,編譯器將列舉全部C
類中名爲f
的方法和其超類中屬性爲public
且名爲f
的方法(超類私有沒法訪問)。f
,就選擇這個方法,這個過程被稱爲重載解析(overloading resoluton)。這個過程容許類型轉換(int轉double,Manager轉Employee等等)。若是編譯器沒有找到,或者發現類型轉換後,有多個方法與之匹配,就會報告一個錯誤。private
方法,static
方法、final
方法或者構造器,編譯器將能夠準確知道應該調用哪一個方法,這種稱爲靜態綁定(static binding)。若是不是這些,那調用的方法依賴於隱式參數的實際類型,而且在運行時動態綁定,好比x.f(args)
這個例子。x
所引用對象的實際類型最合適的那個類的方法。好比x
實際是C
類,它是D
類的子類,若是C
類定義了方法f(String)
,就直接調用;不然將在C
類的超類中尋找,以此類推。簡單說就是順着繼承層次從下到上的尋找方法。若是每次調用方法都要深度/廣度遍歷搜索繼承鏈,時間開銷很是大。
所以虛擬機預先爲每一個類建立一個方法表(method table),其中列出了全部方法的簽名和實際調用的方法,這樣一來在真正調用時,只須要查表便可。
若是調用super.f(args)
,編譯器將對隱式參數超類的方法表進行搜索。
以前的Employee
類和Manager
類的方法表:
Employee: getName() -> Employee.getName() getSalary() -> Employee.getSalary() getHireDay() -> Employee.getHireDay() raiseSalary(double) -> Employee.raiseSalary(double) Manager: getName() -> Employee.getName() getSalary() -> Manager.getSalary() getHireDay() -> Employee.getHireDay() raiseSalary(double) -> Employee.raiseSalary(double) setBonus(double) -> Manager.setBonus(double)
在運行時,調用e.getSalary()
的解析過程:
Employee e
能夠引用全部Employee
類的子類,因此要肯定實際引用的類型)。getSalary
簽名的類,虛擬機肯定調用哪一個方法。注:在覆蓋一個方法時,子類方法不能低於超類方法的可見性,特別是超類方法是public
,子類覆蓋方法時必定聲明爲public
,由於常常會發生這樣的錯誤:在聲明子類方法時,由於遺漏public
而使編譯器把它解釋爲更嚴格的訪問權限。
有時候,可能但願阻止人們利用某個類定義子類。
不容許擴展的類被稱爲
final
類。
若是在定義類時使用了final
修飾符就代表這個類是final
類。
public final class Executive extends Manager { ... }
方法也能夠被聲明爲final
,這樣子類就不能覆蓋這個方法,final
類中的全部方法自動地稱爲final
方法。
public class Employee { ... public final String getName() { return name; } ... }
這裏注意final
域的區別,final
域指的是構造對象後就再也不運行改變他們的值了,不過若是一個類聲明爲final
,只有其中的方法自動地成爲final
,而不包括域。
將方法或類聲明爲
final
主要目的是:確保不會在子類中改變語義。
有時候就像將浮點數轉換爲整數同樣,也可能須要將某個類的對象引用轉換成另外一個類的對象引用。
對象引用的轉換語法與數值表達式的類型轉換相似,僅須要一對圓括號將目標類名括起來,並放置在須要轉換的對象引用以前就能夠了。
Manager boss = (Manager) staff[0]; // 由於以前把boss這個Manager類對象也存在了Employee數組中 // 如今經過強制類型轉換回覆成Manager類
進行類型轉換的惟一緣由:在暫時忽略對象的實際類型以後,使用對象的所有功能。
在Java中,每一個對象變量都屬於一個類型,類型描述了這個變量所引用的以及可以引用的對象類型。
將一個值存入變量時,編譯器將檢查是否容許該操做:
若是試圖在繼承鏈上進行向下的類型轉換,並謊報有關對象包含的內容(好比硬要把一個Employee
類對象轉換成Manager
類對象):
Manager boss = (Manager) staff[1]; // Error
運行時,Java運行時系統將報告這個錯誤(不是在編譯階段),併產生一個ClassCastException
異常,若是沒有捕獲異常,程序將會終止。
因此應該養成一個良好習慣:在進行類型強轉以前,先查看一下是否能成功轉換,使用instanceof
操做符便可:
if(staff[1] instanceof Manager) { boss = (Manager) staff[1]; ... }
注:若是x
爲null
,則它對任何一個類進行instanceof
返回值都是false
,它由於沒有引用任何對象。
位於上層的類一般更具備通用性,甚至可能更加抽象,對於祖先類,咱們一般只把它做爲派生其餘類的基類,而不做爲想使用的特定的實例類,好比Person
類對於Employee
和Student
類而言。
因爲Person
對子類一無所知,可是又想規範他們,一種作法是提供一個方法,而後返回空的值,另外一種就是使用abstract
關鍵字,這樣Person
就徹底不用實現這個方法了。
public abstract String getDescription(); // no implementation required
爲了提供程序的清晰度,包含一個或多個抽象方法的類自己必須被聲明爲抽象的。
public abstract class Person { private String name; ... public abstract String getDescription(); ... public String getName() { return name; } }
除了抽象方法外,抽象類還能夠包含具體數據和具體方法。
儘管許多人認爲,在抽象類中不能包含具體方法,可是仍是建議儘可能把通用的域和方法(無論是否抽象)都放在超類(無論是否抽象)中。
雖然你能夠聲明一個抽象類的引用變量,可是隻能引用非抽象子類的對象,由於抽象類不能被實例化。
在非抽象子類中定義抽象類的方法:
public class Student extends Person { private String major; ... public String getDescription() { return "a student majoring in " + major; } }
儘管Person
類中沒有具體定義getDescription
的具體內容,可是當一個Person
類型引用變量p
使用p.getDescription()
也是沒有問題的,由於根據前面的方法調用過程,在運行時,方法的實際尋找是從實際類型開始尋找的,而實際類型都是定義了這個方法的具體內容。
那你可能會問,我能夠只在Student
類中定義getDescription
不就好了麼?爲何還要在Person
去聲明?由於若是這樣的話,就不能經過p調用getDescription
方法了,由於編譯器只容許調用在類中聲明的方法。
有些時候,人們但願超類中的某些方法容許被子類訪問,或容許子類的方法訪問超類中的某個域,而不讓其餘類訪問到。
爲此,須要將這些方法或域聲明爲protected
。
例如,若是Employee
中的hireDay
聲明爲protected
,而不是private
,則Manager
中的方法就能夠直接訪問它。
不過,Manager
中的方法只可以訪問Manager
對象中的hireDay
,而不能訪問其餘Employee
對象中的這個域,這樣使得子類只能得到訪問受保護域的權利。
對於受保護的域來講,可是這在必定程度上違背了OOP提倡的數據封裝原則,由於若是當一個超類進行了一些修改,就必須通知全部使用這個類的程序員(而不像普通的private
域,只能經過開放的方法去訪問)。
相比較,受保護的方法更具備實際意義。若是須要限制一個方法的使用,就能夠聲明爲protected
,這代表子類獲得信任,能夠正確地使用這個方法,而其餘類(非子類)不行。
這種方法的一個最好示例就是Object
類中的clone
方法。
概括總結Java控制可見性的4個訪問修飾符:
public
:對全部類可見protected
:對本包和全部子類可見private
:僅對本類可見
Obejct
類是Java中全部類的始祖,Java中每一個類都是它擴展而來。
若是沒有明確指出超類,Object就被認爲是這個類的超類。
天然地,可使用Object
類型的變量引用任何類型的對象:
Obejct obj = new Employee("Harry Hacker", 35000);
Object
類型的變量只能用於各類值的通用持有者。若是想要對其中的內容進行具體操做,還須要清楚對象的原始類型,並進行相應的類型轉換:
Employee e = (Employee) obj;
Object
類中的equals
方法用於檢測一個對象 是否等於另一個對象。
這裏的等於指的是判斷兩個對象是否具備相同的引用。
可是在判斷兩個不肯定是否爲null
的對象是否相等時,須要使用Objects.equals
方法,若是兩個都是null
,將返回true
;若是其中一個爲null
,另外一個不是,則返回false
;若是兩個都不爲null
,則調用a.equals(b)
。
固然大多數時候Object.equals
並不能知足,通常來講咱們須要比較兩個對象的狀態是否相等,這個時候須要重寫這個方法:
public class Manager extends Employee { ... public boolean equals(Object otherObject) { // 首先要調用超類的equals if(!super.equals(otherObejct)) return false; Manager other = (Manager) otherObject; return bonus == other.bonus; } }
在閱讀後面的書籍筆記內容以前,首先補充一下getClass
和instanceof
究竟是什麼:
object
的運行時類Class
(Java中有一個類叫Class)。好比一個Person
變量p
,則p.getClass()
返回的就是Person
這個類的Class
對象,Class
類提供了不少方法來獲取這個類的相關信息manager instanceof Employee
是返回true
的好了,讓咱們回到原書吧。
若是隱式和顯示的參數不屬於同一個類,equals
方法如何處理呢?
有許多程序員喜歡使用instanceof
來進行檢測:
if(!otherObject instanceof Employee) return false;
這樣作不但沒有解決otherObject
是子類的狀況,而且還可能招致一些麻煩。
Java語言規範要求equals
方法具備下面的特性:
x
,x.equals(x)
應返回true
x
和y
,y.equals(x)
返回true
,則x.equals(y)
也應該返回true
x
,y
和z
,若是x.equals(y)
返回true
,y.equals(z)
返回true
,則x.equals(z)
也應該返回true
x
和y
引用對象沒有發生變化,反覆調用x.equals(y)
應該返回一樣結果x
,x.equals(null)
應該返回false
從兩個不一樣的狀況看一下這個問題:
getClass
進行檢測instanceof
進行檢測,這樣能夠在不一樣子類的對象之間進行相等的比較給出一個編寫完美equals
方法的建議:
otherObejct
,稍後將它轉換成另外一個叫作other
的變量檢測this
與otherObject
是否因用同一個對象:
if(this == otherObject) return true;
檢測otherObject
是否爲null
:
if(otherObject == null) return false;
比較this
和otherObject
是否屬於同一個類
// 若是equals語義在每一個子類中有改變,就用getClass if(getClass() != otherObject.getClass()) return false; // 若是子類擁有統一的語義,就用instanceof檢測 if(!(otherObejct instanceof ClassName)) return false;
將otherObejct
轉換爲相應的類類型變量:
ClassName other = (ClassName) otherObejct;
開始進行域的比較,使用==
比較基本類型域,使用Objects.equals
比較對象域
return field1 == other.field1 && Objects.equals(field2, other.field2) && ...;
若是在子類中從新定義equals
,還要在其中包含調用super.equals(other)
。
另外,對於數組類型的域,可使用靜態的Array.equals
方法檢測相應的數組元素是否相等。
散列碼(hash code)是由對象導出的一個整數值。
散列碼是沒有規律的,若是x
和y
是兩個不一樣的對象,x.hashCode()
與y.hashCode()
基本上不會相同。
對於String
類而言,字符串的散列碼是由內容導出的。
因爲hashCode方法定義在Object
類中,所以每一個對象都有一個默認的散列碼,其值爲對象的存儲地址。
若是從新定義equals
方法,就必須從新定義hashCode
方法,以便用戶能夠將對象插入到散列表中。
hashCode
方法應該返回一個整數數值(也能夠是負數),併合理地組合實例域的散列碼,以便讓各個不一樣的對象產生的散列碼更加均勻。
例如,Employee
類的hashCode
方法:
public class Employee { public int hashCode() { return 7 * name.hashCde() + 11 * new Double(salary).hashCode() + 13* hireDay.hashCode(); } }
不過若是使用null
安全的方法Objects.hashCode(...)
就更好了,若是參數爲null
,這個方法返回0。
另外,使用靜態方法Double.hashCode(salary)
來避免建立Double
對象。
還有更好的作法,須要組合多個散列值時,能夠調用Objects.hash
並提供多個參數。
public int hashCode() { return Obejcts.hash(name, salary, hireDay); }
equals
與hashCode
的定義必須一致:若是x.equals(y)
返回true
,那麼x.hashCode()
就必須與y.hashCode()
具備相同的值。
Object
中還有一個重要的方法,就是toString
方法,它用於返回表示對象值的字符串。
絕大多數(但不是所有)的toString
方法都遵循這樣的格式:類的名字,隨後是一對方括號括起來的域值。
public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; }
toString
方法也能夠供子類調用。
固然,設計子類的程序員也應該定義本身的toString
方法,並將子類域的描述添加進去。
若是超類使用了getClass().getName()
,子類只須要調用super.toString()
便可。
public class Manager extends Employee { ... public String toString() { return super.toString() + "[bonus=" + bonus + "]"; } }
如今,Manager
對象將打印輸出以下所示內容:
Manager[name=...,salary=...,hireDay=...][bonus=...]
注意這裏在子類中調用的super.toString()
,不是在超類Employee
中調用的麼?爲何打印出來的是Manager
?
由於getClass
正如前面所說,獲取的是這個對象運行時的類,與在哪一個類中調用無關。
若是任何一個對象x
,調用System.out.println(x)
時,println
方法就會直接調用x.toString()
,並打印輸出獲得的字符串。
Object
類定義了toString
方法,用來打印輸出對象所屬類名和散列碼:
System.out.println(System.out) // 輸出 java.io.PrintStream@2f6684
這樣的結果是PrintStream
類設計者沒有覆蓋toString
方法。
對於一個數組而言,它繼承了object
類的toString
方法,數組類型按照舊的格式打印:
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 }; String s = "" + luckyNumbers; // s [I@1a46e30
前綴[I
代表是一個整形數組,若是想要獲得裏面內容的字符串,應該使用Arrays.toString
:
String s = Arrays.toString(luckyNumbers); // s [2,3,5,7,11,13]
若是想要打印多維數組,應該使用Arrays.deepToString
方法。
強烈建議爲自定義的每個類增長
toString
方法。
在許多程序設計語言中,必須在編譯時就肯定整個數組大小。
在Java中,容許運行時肯定數組的大小:
int actualSize = ...; Employee[] staff = new Employee[actualSize];
固然,這段代碼並無徹底解決運行時動態更改數組的問題。一旦肯定了大小,想要改變就不容易了。
在Java中,最簡單的解決方法是使用Java中另外一個被稱爲ArrayList
的類,它使用起來有點像數組,但在添加或刪除元素時,具備自動調節數組容量的功能,而不須要爲此編寫任何代碼。
ArrayList
是一個採用類型參數(type paraneter)的泛型類(generic class)。爲了指定數組列表保存的元素對象類型,須要用一對尖括號將類名括起來加在後面,例如ArrayList<Employee>
。
ArrayList<Employee> staff = new ArrayList<Employee>(); // 兩邊都是用參數有些繁瑣,在Java SE 7中,能夠省去右邊的類型參數 ArrayList<Employee> staff = new ArrayList<>();
這通常叫作「菱形語法」(<>
),能夠結合new
操做符使用。
若是賦值給一個變量,或傳遞到某個方法,或者從某個方法返回,編譯器會檢查這個變量、參數或方法的泛型類型,而後將這個類型放在<>
中。
在這個例子中,new ArrayList<>()
將賦值給一個類型爲ArrayList<Employee>
的變量,因此泛型類型爲Employee
。
使用add
方法能夠將元素添加到數組列表中。
staff.add(new Employee(...));
數組列表管理着對象引用的一個內部數組,最終數組空間有可能被用盡,這時數組列表將會自動建立一個更大的數組,並將全部的對象從較小數組中拷貝到較大數組中。
也能夠肯定存儲的元素數量,在填充數組前調用ensureCapacity
方法:
// 分配一個包含100個對象的內部數組 // 在100次調用add時不用再每次都從新分配空間 staff.ensureCapacity(100); // 固然也能夠經過把初始容量傳遞給構造器實現 ArrayList<Employee> staff = new ArrayList<>(100);
size方法返回數組列表包含的實際元素數目:
staff.size()
一旦可以確認數組列表大小再也不發生變化,能夠調用trimToSize
方法。這個方法將存儲區域的大小調整爲當前元素數量所須要的存儲空間數目,垃圾回收器將回收多餘的存儲空間。
數組列表自動擴展容量的便利增長了訪問元素語法的複雜程度。
須要使用get
和set
方法實現或改變數組元素的操做,而不是[index]
語法格式。
staff.set(i, harry); Employee e = staff.get(i);
當沒有泛型類時,原始的ArrayList
類提供的get
方法別無選擇只能返回Object
,所以,get
方法的調用者必須對返回值進行類型轉換:
Employee e = (Employee) staff.get(i);
固然仍是有一個比較方便的方法來靈活擴展又方便訪問:
ArrayList<X> list = new ArrayList<>(); while(...) { x = ...; list.add(x); } X[] a = new X[list.size()]; // 使用toArray方法把數組元素拷貝到一個數組中 list.toArray(a);
還能夠在數組列表的中間插入元素:
int n = staff.size()/2; staff.add(n, e);
固然也能夠刪除一個元素:
Employee e = staff.remove(n);
可使用for each
循環遍歷數組列表:
for(Employee e : staff) do sth with e
有時須要將int
這樣的基本類型轉換爲對象,全部基本類型都有一個與之對應的類。
例如,Integer
類對應基本類型int
,一般這些類稱爲包裝器(wrapper)。
這些對象包裝器有很明顯的名字:Integer
、Long
、Float
、Double
、Short
、Byte
、Character
、Void
和Boolean
(前6個類派生於公共超類Number
)。
對象包裝器類是不可變的,即一旦構造了包裝器,就不容許更改包裝在其中的值。
同時,對象包裝器類仍是final
,所以不能定義它們的子類。
有一個頗有用的特性,便於添加int
類型的元素到ArrayList<Integer>
中。
ArrayList<Integer> list = new ArrayList<>(); list.add(3); // 這裏將自動地變爲 list.add(Integer.valueOf(3));
這種變換被稱爲自動裝箱(autoboxing)。
相反地,將一個Integer
對象賦給一個int
值時,將會自動地拆箱。
int n = list.get(i); // 將會被翻譯成 int n = list.get(i).intValue();
在算術表達式中也能自動地裝箱和拆箱,例如自增操做符應用於一個包裝器引用:
Integer n = 3; n++;
編譯器自動地插入一條對象拆箱指令,而後自增,而後再結果裝箱。
==
雖然也能夠用於對象包裝器對象,但通常檢測是對象是否指向同一個存儲區域。
Integer a = 1000; Integer b = 1000; if(a == b) ...;
然而Java中上面的判斷是有可能(may)成立的(這也太玄學了),因此解決辦法通常是使用equals
方法。
還有一些須要強調的:
null
,因此自動裝箱可能會拋出NullPointerException
異常Integer
和Double
類型,Integer
值就會拆箱,提高爲double
,再裝箱爲Double
使用數值對象包裝器還有另一個好處,能夠將某些基本方法放置在包裝器中,好比,將一個數字字符串轉換成數值。
int x = Integer.parseInt(s);
Java SE 5之前的版本中,每一個Java方法都有固定數量的參數,然而如今的版本提供了可變的參數數量調用的方法。
好比printf
方法的定義:
public class PrintStream { public PrintStream printf(String fmt, Object... args) { return format(fmt, args); } }
這裏的省略號...
是Java代碼的一部分,代表這個方法能夠接收任意數量的對象(除fmt參數外)。
實際上,printf
方法接收兩個參數,一個是格式字符串,另外一個是Object[]
數組,其中保存着全部的參數。
編譯器須要對printf
的每次調用進行轉換,以便將參數綁定到數組上,並在必要的時候進行自動裝箱:
System.out.printf("%d %s", new Object[]{ new Integer(n), "widgets" });
用戶也能夠自定義可變參數的方法,並將參數指定爲任意類型,甚至基本類型。
// 找出最大值 public static double max(double... values) { double largest = Double.NEGATIVE_INFINITY; for(double v : values) if(v > largest) largest = v; return largest; }
public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };
實際上這個聲明定義的類型是一個類,它恰好有4個實例。
所以比較兩個枚舉類型值時,不須要調用equals
,直接使用==
就能夠了。
若是須要的話,能夠在枚舉類型中添加一些構造器、方法和域,構造器只在構造枚舉常量的時候被調用。
public enum Size { SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL"); private String abbreviation; private Size(String abbreviation) { this.abbreviation = abbreviation; } public String getAbbreviation() { return abbreviation; } }
全部的枚舉類型都是Enum
類的子類,他們集成了這個類的許多方法,最有用的一個是toString
,這個方法能返回枚舉常量名,例如Size.SMALL.toString()
返回"SMALL"
。
toString
的逆方法是靜態方法valueOf
。
Size s = Enum.valueOf(Size.class, "SMALL");
將s
設置成Size.SMALL
。
每一個枚舉類型都有一個靜態的values
方法,返回一個包含所有枚舉值的數組。
Sizep[] values = Size.values();
ordinal
方法返回enum
聲明中枚舉常量的位置,位置從0開始技術。
反射是一種功能強大且複雜的機制,使用它的主要人員是工具構造者,而不是應用程序員。
因此這部分先跳過,將會在之後一個專題單獨來講明。
"is-a"
關係protected
受保護訪問Object
全部類的超類equals
方法hashCode
方法toString
方法我的靜態博客: