繼承
利用繼承,人們可以基于已存在的類構(gòu)造一個(gè)新類,繼承已存在的類就是復(fù)用(繼承)這些類的方法和域。在此基礎(chǔ)上,還可以添加一下新的方法和域,以滿足新的需求。
覆蓋方法
簡(jiǎn)答的代碼示例:
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;
this.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() {
return "Employee{" +
"name='" + name + '\'' +
", salary=" + salary +
", hireDay=" + hireDay +
'}';
}
}
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, int year, int month, int day) {
// Manager 類的構(gòu)造器不能訪問 Employee 類的私有域,所以必須利用 Employee 類的構(gòu)造器對(duì)這部分私有域進(jìn)行初始化, 且使用 super 調(diào)用構(gòu)造器的語句必須是子類構(gòu)造器的第一條語句。
super(name, salary, year, month, day);
this.bonus = 0;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public double geSalary() {
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
}
上述兩個(gè)類中,Manager 和 Employee 之間是 “is-a" 的關(guān)系” “is-a”是繼承的一個(gè)明顯特征, 根據(jù)繼承的定義, Manager 類繼承了 name, salary, hireDay 三個(gè)域以及對(duì)應(yīng)的三個(gè) get 訪問器方法。將通用的方法放在超類中,而將具有特殊用途的方法放在子類中,在面向?qū)ο蟪绦蛟O(shè)計(jì)中十分普遍。
需要注意:
Manager 類的 getSalary 方法不能直接訪問父類的私有域, 只有父類才能訪問其私有域,因此需要父類提供一個(gè)訪問 salary 的公共接口,通過 super 關(guān)鍵字調(diào)用。
super 與 this 的不同:
super 不是一個(gè)對(duì)象的引用,不能將 super 付給另一個(gè)對(duì)象變量,它只是一個(gè)指示編譯期調(diào)用超類方法的特殊關(guān)鍵字。
父類與子類構(gòu)造器的關(guān)系
- 當(dāng)子類沒有帶參構(gòu)造器和無參構(gòu)造器,父類有帶參構(gòu)造器和無參構(gòu)造器時(shí), 子類會(huì)有默認(rèn)的無參構(gòu)造器,并且該無參構(gòu)造器會(huì)默認(rèn)調(diào)用父類無參構(gòu)造器的方法。
public class ExtendsTest {
public static void main(String[] args) {
Son son = new Son();
}
}
class Father {
private int age;
public Father() {
System.out.println("Father no argument constructor");
}
public Father(int age) {
this.age = age;
}
}
class Son extends Father {
}
// output
// Father no argument constructor
- 當(dāng)子類沒有帶參構(gòu)造器和無參構(gòu)器,父類只有帶參構(gòu)造器時(shí)編譯時(shí)檢查會(huì)報(bào)錯(cuò),因?yàn)楫?dāng)寫了帶參構(gòu)造器時(shí)系統(tǒng)不會(huì)給出默認(rèn)的無參構(gòu)造器,此時(shí)子類不能調(diào)用父類的默認(rèn)無參構(gòu)造器。
class Father {
private int age;
public Father(int age) {
this.age = age;
}
}
class Son extends Father { // error There is no default constructor available in Father
}
- 當(dāng)子類沒有帶參構(gòu)造器而有無參構(gòu)器,且父類同時(shí)有帶參構(gòu)造器和無參構(gòu)造器時(shí),子類的無參構(gòu)造器在第一行默認(rèn)調(diào)用父類的無參構(gòu)造器。
public class ExtendsTest {
public static void main(String[] args) {
Son son = new Son();
}
}
class Father {
private int age;
public Father() {
System.out.println("Father no argument constructor");
}
public Father(int age) {
this.age = age;
}
}
class Son extends Father {
private String name;
public Son() {
System.out.println("Son no argument constructor");
}
}
// output
// Father no argument constructor
// Son no argument constructor
總結(jié)
- 如果子類的構(gòu)造器沒有顯示地調(diào)用超類的構(gòu)造器,則會(huì)自動(dòng)地超類默認(rèn)(沒有參數(shù))的構(gòu)造器,若父類只存在帶慘構(gòu)造則不會(huì)有默認(rèn)的無參構(gòu)造器。
- 如果父類沒有無參構(gòu)造器,并且子類的構(gòu)造器又沒有顯示地調(diào)用父類的其他構(gòu)造器,則Java編譯期將報(bào)告錯(cuò)誤。
final
如果類用 final 修飾,其他類就不能繼承它,如果方法用 final 修飾,子類就不能重寫它, 如果類中的域用 final 修飾,這個(gè)域在構(gòu)造對(duì)象完成之后就不能被修改。final 修飾符的主要作用為:確保被修飾的對(duì)象在子類中不會(huì)改變語義。
注意:
- final 修飾類時(shí),類中方法自動(dòng)變成 final, 但不包括域。
- 如果方法很簡(jiǎn)單,被頻繁調(diào)用且沒有真正地被覆蓋,那么及時(shí)編譯器會(huì)將這個(gè)方法進(jìn)行內(nèi)聯(lián)處理(inline),例如內(nèi)聯(lián)調(diào)用
e.getName()會(huì)被替換為訪問e.name域。
強(qiáng)制類型轉(zhuǎn)換
進(jìn)行類型轉(zhuǎn)換的唯一原因是:在暫時(shí)忽視對(duì)象的實(shí)際類型之后,使用對(duì)象的全部功能。在 Java 中,每個(gè)對(duì)象變量都屬于一個(gè)類型,類型描述了這個(gè)變量所引用的以及能夠引用的對(duì)象類型。
Java 是強(qiáng)類型語言,在將一個(gè)值存入變量時(shí),編譯器將檢查是否允許該操作,將一個(gè)子類的引用復(fù)制給一個(gè)超類變量,編譯器時(shí)允許的(向上轉(zhuǎn)型,此時(shí)會(huì)丟失子類擴(kuò)展的方法),但將一個(gè)超類的引用復(fù)制給一個(gè)子類變量(向下轉(zhuǎn)型,此時(shí)可以找回子類擴(kuò)展的方法),必須進(jìn)行類型轉(zhuǎn)換,這樣才能通過運(yùn)行時(shí)檢查。
一般只有在使用子類中的特有方法是才需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換,此時(shí)可以使用 instanceof 先檢查類型,再進(jìn)行類型轉(zhuǎn)換。
Employee[] staff = new Employee[3];
Manager boss = new Manager();
staff[0] = boss;
staff[1] = new Employee();
if (staff[1] instanceof Manager) {
boss = (Manager) staff[1];
}
注意:
null instanceof C // false,不會(huì)報(bào)錯(cuò),因?yàn)閚ull沒有引用任何對(duì)象,當(dāng)然也不會(huì)引用C類型對(duì)象
本文章與github上同步,歡迎來玩,提交issue。
Reference
1.[Java核心技術(shù)·卷 I(原書第10版)](繼承