Java繼承知識(shí)點(diǎn)總結(jié)

繼承

利用繼承,人們可以基于已存在的類構(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版)](繼承

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容