例如:
| 代碼如下 |
複製代碼 |
class Manager extends Employee { //Manager 類繼承Employee類,同時添加新的方法setBonus和新的域bonus public void setBonus(double s) { bonus = s; } ... private double bonus; } |
1. 子類方法覆蓋(override)超類方法
假設Manager類繼承於Employee類,Employee有name、salary和hireDay三個域,Manager又新增了bonus一個域。Employee有方法getSalary(),現在Manager的計算薪水的方式不一樣,需要重寫getSalary(),以覆蓋Employee類的此方法。
需要注意的是子類不能直接存取超類的私人域(假設salary為私人域),那麼就不能使用下面這種方法:
public double getSalary()
{
return salary + bonus; //error. salary 不能被訪問到
}
Java利用另一個方法解決上面這個問題,即使用超類的方法簡介擷取域的值。
public double getSalary()
{
return super.getSalary() + bonus;
}
注釋:super不是對象引用,不能把它覆給另一個物件變數,它只是一個指示編譯器調用超類方法的特有關鍵字。
super的另一個應用就是在構造器中。子類構造器需要調用超類的某些構造器以初始化一個些超類的域(尤其是私人域)。
| 代碼如下 |
複製代碼 |
public Manager(String n, double s, int year, int month, int day) { //調用超類的中含有n, s, year, month和day參數的構造器 super(n, s, year, month, day); bonus = 0; } |
2. 繼承層次
由一個公用超類派生出來的所有類的集合被稱為繼承層次(inheritance hierarchy),在繼承層次中由某個特定類到其祖先的路徑被稱為該類的繼承鏈(inheritance chain)。
3. 多態(polymorphism)和動態綁定(dynamic binding)
一個物件變數可以引用多種實際類型的現象稱多態;
在運行時能夠自動地選擇調用哪個方法的現象稱為動態綁定。
例如:
| 代碼如下 |
複製代碼 |
Manager boss = new Manager("boss", 80000, 1987, 12, 15); boss.setBonus(5000); Employee[] staff = new Employee[3]; staff[0] = boss; staff[1] = new Employee("employee_1",50000, 1989,3,23); staff[2] = new Employee("employee_2",50000, 1990,4,22); //多態:變數e既可以引用Manager對象,又可以引用Employee對象 //動態綁定:變數e引用Employee對象時調用Employee類的getSalary()方法 //而引用Manager對象時調用Manager類的getSalary()方法 for(Employee e : staff) System.out.println(e.getName() + " " + e.getSalary()); 多態: Employee e; e = new Employee(...); //OK e = new Manager(...); //OK //但是超類變數不能引用子類對象 Employee a = new Employee(...); Manager m = a; //error動態綁定: |
對象方法執行過程的詳細:
① 編譯器查看對象的宣告類型和方法名,擷取所有可能被調用的候選方法;
② 編譯器查看那調用方法時提供的參數類型,如果與某個候選方法的參數類型完全相同,則選擇這個方法。這個過程被稱為重載解析(overloading resolution);
③ 前面兩步已經獲得需要調用的方法名字和參數類型,接下來如果是private方法、static方法、final方法或者構造器,那麼編譯器將可以準確地知道應該調用哪個方法,這種調用方式被稱為靜態繫結(static binding)。與此對應的是,調用的方法依賴於隱式參數的實際類型,並且在運行時實現動態綁定;
④ 哪個動態綁定調用方法時,虛擬機器一定調用與隱式參數所引用對象的實際類型最合適的那個類的方法。
註:虛擬機器為了避免每次都進行搜尋方法,所以預先為每個類建立一個方法表(method table),其中列舉了所有方法的簽名和實際調用的方法。只需查表,所以更快。
4. 阻止繼承:final類和方法
將類聲明為final,則會其他類對該類進行繼承。將類中的方法聲明為final,子類就不能覆蓋這個方法了。final類中的所有方法自動地稱為final方法。
| 代碼如下 |
複製代碼 |
final class Executive extends Manager { ... }class Employee { ... public final String getName() { return name; } ... } |
注釋:域也可以聲明為final。對於final域來說,只是在構造對象之後不能再修改它的值而已。我們將方法或者類聲明為final的主要原因是確保他們不會在子類中改變語義。有些程式員建議是除非我們有足夠的理由使用多態性,否則應該將所有的方法聲明為final。這樣可以減少動態綁定過程帶來的系統開銷,提高程式效能。但是,隨著Java虛擬機器的發展,其處理能力越來越強,編譯器已經可以很短的時間內知道類之間的繼承關係,並且能夠很快地檢測出是否存在覆蓋的方法。
5. 強制類型轉換
•只能在繼承層次內進行類型轉換
•在將超類轉換成子類之前,應該使用instanceof進行檢查
| 代碼如下 |
複製代碼 |
Employee[] staff = new Employee[3]; staff[0] = new Manager(...); staff[1] = new Employee(...); staff[2] = new Employee(...); Manager boss; if(staff[1] instanceof Manager) { boss = (Manager) staff[1]; } |
6. 抽象類別
抽象類別和抽象方法都用abstract關鍵字聲明。
包含一個或多個抽象方法的類必須聲明為抽象類別。
在抽象類別中還可以有域和具體方法的實現。
抽象類別不能被執行個體化,即不能建立抽象類別的對象。
| 代碼如下 |
複製代碼 |
abstract class Person { public Person(String n) { name = n; } public abstract String getDescription(); public String getName() { return name; } private String name; } |
需要注意的是雖然我們不能將抽象類別執行個體化,但是可以定義一個抽象類別的物件變數,但它只能引用非抽象子類的對象,例如:
//這裡的Student類是Person類的子類
Person p = new Student("Raysmond","1024");7. 受保護訪問
•private —— 僅對本類可見
•public —— 對所有類可見
•protected —— 對本包和所有子類可見
•預設情況下是對本包可見
8. 所類的超類——Object類
Java中所有類的超類是Object,這是不需要在類中聲明的,Java中每一個類都由它派生出來。需要注意兩點。
•可以使用Object類型的變數引用任何對象
Object obj = new Student("Raysmond","1024");•在Java中只有基本類型不是對象,其他資料類型,例如數組,都是對象,都拓展於Object類。