1. 程式人生 > 實用技巧 >04-繼承

04-繼承

繼承

Java使用extends關鍵字來實現繼承:

Java沒有明確寫extends的類,編譯器會自動加上extends Object

Java只允許一個class繼承自一個類,因此,一個類有且僅有一個父類。只有Object特殊,它沒有父類。

class Person {
    private String name;
    private int age;
}

class Student extends Person {
    private String id;
}

子類自動獲得了父類的所有欄位,嚴禁定義與父類重名的欄位!

protect

繼承有個特點,就是子類無法訪問父類的private欄位或者private方法。例如,Student

類就無法訪問Person類的nameage欄位

為了讓子類可以訪問父類的欄位,我們需要把private改為protected。用protected修飾的欄位可以被子類訪問

protected關鍵字可以把欄位和方法的訪問許可權控制在繼承樹內部,一個protected欄位和方法可以被其子類,以及子類的子類所訪問

class Person {
    protected String name;
    protected int age;
}

class Student extends Person {
    public String hello() {
        return "Hello, " + name; // OK!
    }
}

super

super關鍵字表示父類(超類)。子類引用父類的欄位時,可以用super.fieldName

class Student extends Person {
    public String hello() {
        return "Hello, " + super.name;
    }
}

在Java中,任何class的構造方法,第一行語句必須是呼叫父類的構造方法。如果沒有明確地呼叫父類的構造方法,編譯器會幫我們自動加一句super();,所以,Student類的構造方法實際上是這樣:

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(); // 自動呼叫父類的構造方法
        this.score = score;
    }
}

如果父類沒有預設的構造方法,子類就必須顯式呼叫super()並給出引數以便讓編譯器定位到父類的一個合適的構造方法。

這裡還順帶引出了另一個問題:即子類不會繼承任何父類的構造方法。子類預設的構造方法是編譯器自動生成的,不是繼承的。

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 呼叫父類的構造方法Person(String, int)
        this.score = score;
    }
}

阻止繼承

正常情況下,只要某個class沒有final修飾符,那麼任何類都可以從該class繼承。

向上轉型

Person p = new Student(); // 

這是因為Student繼承自Person,因此,它擁有Person的全部功能

這種把一個子類型別安全地變為父類型別的賦值,被稱為向上轉型(upcasting)

向上轉型實際上是把一個子型別安全地變為更加抽象的父型別:

注意到繼承樹是Student > Person > Object,所以,可以把Student型別轉型為Person,或者更高層次的Object

向下轉型

和向上轉型相反,如果把一個父類型別強制轉型為子類型別,就是向下轉型(downcasting)

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!

子類功能比父類多,多的功能無法憑空變出來。因此,向下轉型很可能會失敗。失敗的時候,Java虛擬機器會報ClassCastException。

為了避免向下轉型出錯,Java提供了instanceof操作符,可以先判斷一個例項究竟是不是某種型別:

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false

instanceof實際上判斷一個變數所指向的例項是否是指定型別,或者這個型別的子類。如果一個引用變數為null,那麼對任何instanceof的判斷都為false