「is-a」 關係是繼承的一個明顯特徵。java
註釋: 咱們使用員工和經理的例子,不過這個例子要有所保留。在真實的世界裏,員工也可能會成爲經理,因此你建模時可能但願經理也是員工,而不是員工的一個子類。不過,在例子中,假設公司只有兩類人:一些人永遠是員工,另外一些人一直是經理。程序員
Employee 類:數組
import java.time.LocalDate; import java.util.Objects; public class Employee { private String name; private double salary; private LocalDate hireDay; public Employee(String name, double salary, int year, int month, int day) { this.name = name; this.salary = salary; hireDay = LocalDate.of(year, month, day); } public String getName() {return name;} public double getSalary() {return salary;} public LocalDate getHireDay() {return hireDay;} public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } @Override public String toString() { String str = "%s[name=%s, salary=%.2f, hireDay=%s]"; return String.format(str, getClass(), name, this.getSalary(), hireDay); } }
Manager 類:ide
public class Manager extends Employee { private double bonus; public Manager(String name, double salary, int year, int month, int day) { super(name, salary, year, month, day); this.bonus = 0; } public void setBonus(double bonus) { this.bonus = bonus; } @Override public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } }
繼承 Employee 類來定義 Manager 類,使用關鍵字 extends 表示繼承。this
public class Manager extends Employee { // added methods and fields }
在 Java 中,全部的繼承都是公共繼承。設計
關鍵字 extends 代表正在構造的新類派生於一個已存在的類。已存在的類稱爲超類(superclass)、基類(base class)或父類(parent class);新類稱爲子類(subclass)、派生類(derived class)或孩子類(child class)。超類和子類是 Java 程序員最經常使用的兩個術語,而瞭解其餘語言的程序員可能更加偏心使用父類和孩子類,這可能很貼切地體現了「繼承」。code
儘管 Employee 類是一個超類,但並非由於它優於子類或者擁有比子類更多的功能。實際上偏偏相反,子類比超類擁有的功能更多。例如,讀過 Manager 類的源代碼以後就會發現,Manager 類比超類 Employee 封裝了更多的數據,擁有更多的功能。orm
註釋: 前綴 「超」(super) 和 「子」(sub) 來源於計算機科學與數學理論中集合語言的術語。全部員工組成的集合包含全部經理組成的集合。能夠這樣說,員工集合是經理集合的超集,也能夠說,經理集合是員工集合的子集。對象
在 Manager 類中,增長了一個用於存儲獎金信息的字段,以及一個用於設置這個字段的新方法 :blog
public class Manager extends Employee { // 獎金 private double bonus; ... public void setBonus(double bonus) { this.bonus = bonus; } }
這裏定義的方法和字段並無什麼特別之處。若是有一個 Manager 對象,就可使用 setBonus 方法。
Manager boss = ...; boss.setBonus(5000);
因爲 setBonus 方法不是在 Employee 類中定義的,全部屬於 Employee 類的對象不能使用它。
然而,儘管在 Manager 類中沒有顯式地定義 getName 和 getHireDay 等方法,可是能夠對 Manager 對象使用這些方法,這是由於 Manager 類自動地繼承了超類 Employee 中的這些方法。
相似地,從超類中還繼承了 name、salary 和 hireDay 這 3 個字段。這樣一來,每一個 Manager 對象就包含了 4 個字段:name、salary、hireDay 和 bonus。
經過擴招超類定義子類的時候,只須要指出子類與超類的不一樣之處。所以在設計類的時候,應當將最通常的方法放在超類中,而將更特殊的方法放在子類中,這種將通用功能抽取到超類的作法在面向對象設計中十分廣泛。
超類中的有些方法對子類 Manager 並不必定適用。具體來講,Manager 類中的 getSalary 方法應該返回薪水和獎金的總和。爲此,須要提供一個新的方法覆蓋(override)超類中的這個方法:
public class Manager extends Employee { ... public dobule getSalary() { ... } }
應該如何實現這個方法呢?乍看起來彷佛很簡單,只要返回 salary 和 bonus 字段的總和就能夠了:
public double getSalary() { return salary + bonus; // 不能運行 }
不過,這樣作是不行的。回想一下,只有 Employee 方法能直接訪問 Employee 類的私有字段。這意味着,Manager 類的 getSalary 方法不能直接訪問 salary 字段。若是 Manager 類的方法想要訪問那些私有字段,就要像全部其餘方法同樣使用公共接口,在這裏就是要使用 Employee 類中的公共方法 getSalary。
如今,再試一下。你須要調用 getSalary 方法而不是直接訪問 salary 字段:
public double getSalary() { double baseSalary = getSalary(); // 仍然不能運行 return baseSalary + bonus; }
這段代碼有問題。問題出如今調用 getSalary 的語句上,它只是在調用自身,這是由於 Manager 類也有一個 getSalary 方法,因此這條語句將會致使無限次調用本身,直到整個程序最終崩潰。
這裏須要指出:咱們但願調用超類 Employee 中的 getSalary 方法,而不是當前類的這個方法。爲此,可使用特殊的關鍵字 super 解決這個問題:
super.getSalary()
這個語句調用的是 Employee 類中的 getSalary 方法。下面是 Manager 類中 getSalary 方法的正確版本:
public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; }
註釋: 有些人認爲 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 具備不一樣的含義。語句
super(name, salary, year, month, day);
是 「調用超類中含有 name、 salary、year、month 和 day 參數的構造器」 的簡寫形式。
因爲 Manager 類的構造器不能訪問 Employee 類的私有字段,因此必須經過一個構造器來初始化這些私有字段。能夠利用特殊的 super 語法調用這個構造器。使用 super 調用構造器的語句必須是子類構造器的第一條語句。
若是子類的構造器沒有顯式地調用超類的構造器,將自動地調用超類的無參數構造器。若是超類沒有無參數構造器,而且在子類的構造器中又沒有顯式地調用超類的其餘構造器,則 Java 編譯器將報告錯誤。
this 關鍵字有兩個含義:
super 關鍵字有兩個含義:
在調用構造器的時候,this 和 super 這兩個關鍵字緊密相關。調用構造器的語句只能做爲另外一個構造器的第一條語句出現。構造器參數能夠傳遞給當前類(this)的另外一個構造器,也能夠傳遞給超類(super)的構造器。
從新定義 Manager 對象的 getSalary 方法以後,獎金將會自動添加到經理的薪水中。
下面給出一個例子說明這個類的使用。咱們要建立一個新經理,並設置他的獎金:
Manager boss = new Manager("Carl Cracker", 75000, 1987, 12, 15); boss.setBonus(500000);
下面定義一個包含 3 個員工的數組:
Employee[] staff = new Employee[3];
在數組中混合填入經理和員工:
staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);
輸出每一個人的信息:
for (Employee e : staff) { System.out.println(e); }
運行這條循環語句將會輸出下列數據:
Carl Cracker 575000.0 Harry Hacker 50000.0 Tommy Tester 40000.0
這裏的 staff[1] 和 staff[2] 僅輸出了基本薪水,這是由於它們是 Employee 對象,而 staff[0] 是一個 Manager 對象,它的 getSalary 方法會將獎金和基本薪水相加。
須要提醒你們的是,如下調用
e.getSalay()
可以選出應該執行的正確 getSalary 方法。請注意,儘管這裏將 e 聲明爲 Employee 類型,但實際上 e 便可以引用 Employee 類型的對象,也能夠引用 Manager 類型的對象。
當 e 應用 Employee 對象時,e.getSalary() 調用的是 Employee 類中的 getSalary 方法;當 e 引用 Manager 對象時,e.getSalary() 調用的是 Manager 類中的 getSalary 方法。虛擬機知道 e 實際引用的對象類型,所以可以正確地調用相應的方法。
一個對象變量能夠指示多種實際類型的現象被稱爲多態(polymorphism)。在運行時可以自動地選擇調用哪一個方法的現象稱爲動態綁定(dynamic binding)。
在 Java 中,動態綁定是默認的行爲。若是不但願讓一個方法虛擬的,能夠將它標記爲 final。
繼承並不只限於一個層次。例如,能夠由 Manager 類派生 Executive 類。
由一個公共超類派生出來的全部類的集合被稱爲繼承層次(inheritance hierarchy),在繼承層次中,從某個特定的類到其祖先的路徑稱爲該類的繼承鏈(inheritance chain)。
一般,一個祖先類能夠擁有多個子孫繼承鏈。例如,能夠由 Employee 類派生出子類 Programmer 和 Secretary,它們與 Manager 類沒有任何關係(它們彼此之間也沒有任何關係)。必要的話,能夠將這個過程一直延續下去。
Java 不支持多重繼承,但提供了一些相似多重繼承的功能——接口。