【Java基礎】-【面向對象】-繼承

繼承是衆多關係中的一種,它與組合都可以實現代碼複用的思想,只是其原理截然不同:所謂組合,是指在新的類中持有其他類對象的引用,可以理解爲‘我’使用‘你’來達到目的,而繼承則是一種更爲細緻的關係,可以理解爲父子關係,繼承是面向對象程序設計的核心思想之一,在java中,支持的是單繼承。

一個類A繼承了另一個類B,B稱之爲基類(又叫父類),A成爲子類,子類A自動擁有父類的所有(成員變量和方法),但這並不表明子類可以看到或隨意使用父類的東西。

當你創建一個類的時候,實際上一定會使用到繼承,因爲,除非你已經指定了繼承的父類,否則它一定是繼承自Object類,Object類是所有類的直接或間接父類。

繼承使用extends關鍵字實現,例如:

class Parents {
public Parents() {}
}

class Childs extends Parents {
public Childs() {}
}

有一些父類成員對於子類來說是不可見的、不可用的,雖然子類確實已經得到了父類的所有成員,但是如果父類中的成員變量 或者 方法被聲明稱了private權限,那麼它就是對子類不可見的,子類無法使用私有的東西。

在繼承的過程當中,子類對象內部其實包含了一個父類對象,在子類對象中使用super關鍵字可以調用父類對象中的可用成員,我們通過一個內存圖來了解這個過程:

public class Test1 {
public static void main(String[] args) {

Childs c = new Childs();
c.setAge(21);
c.setSchools("SCHOOL");
c.getParentsAge();

}
}
class Parents {
int age = 45;
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
class Childs extends Parents {
String schools;
public void setSchools(String schools) {
this.schools = schools;
}
public String getSchools() {
return schools;
}
public void getParentsAge() {
System.out.println(super.age);
}
}

如上所示的程序,一個父類有age成員變量,其子類自然也繼承了這個變量(實際上這個變量是子類對象內部的父類對象的),同時這個子類對象還有一個自己的成員變量school,從main方法開始我們畫內存:

上圖中的兩塊內存分別是棧內存和堆內存,棧內存是專門存放局部變量的,堆內存是用來存放new出來的對象的,堆內存是動態分配內存。

那麼根據代碼中,我們首先new出來一個Childs對象c,c則是存放在棧中的,其內部存儲了對象位於堆中的地址,也就是他指向了堆中的Childs對象。然後由於Childs對象是繼承自Parents對象的,所以在實例化Childs對象的同時,實際上已經創建了一個Parents對象位於Childs對象的內部,然後我們爲c進行賦值,最後我們在Childs對象的內部使用super關鍵字獲取父類的年齡,結果是21。

到此,你所需要知道的是當創建了一個導出類(子類)的時候,該對象包含了一個基類的對象,這個子對象與你直接使用基類創建一個對象是一樣的,唯一的區別就是一個來自內部,後者來自於外部。而基類子對象被包裝在導出類對象的內部。

在繼承關係中,還有一點需要非常注意的就是繼承過程的構造器,java中規定,子類構造器中必須調用基類的構造器,這其實也很容易理解,因爲上面我們已經說過,子類對象內部實際上包含着一個基類對象,也就是說,子類實例化的時候必須也要實例化一個基類對象,實例化必然調用對應的構造器!同時,在子類構造器中,寫在第一行的(要麼不寫,系統默認調用基類的無參構造器,如果基類沒有無參構造器,則編譯出錯!)、首先執行的必定是基類構造方法,有父纔有子嘛!

我們可以使用super關鍵字調用父類的構造方法(必須寫在第一行),如果子類未顯式調用基類構造方法,同時基類沒有無參構造方法,則編譯出錯!

繼承關係中還有一個知識點需要特別說明,那就是“重寫”(overwrite)的概念,所謂重寫,就是子類中可以根據需要對從基類中繼承的方法進行重新編寫,自己定製內部邏輯。重寫方法必須和被重寫方法有相同的方法名參數列表返回類型!同時,重寫的方法不能有比被重寫方法更加嚴格的訪問權限!在涉及到重寫的時候,我們通常會使用@Override註解,這個註解可以讓你及時糾正把重寫不小心寫成了重載(overload)。

最後一個概念就是向上轉型,這個概念將會在介紹接口的時候更加詳細的說明。首先,你要知道什麼是向上轉型。

繼承技術最重要的方面是用來表現新類和基類之間的關係,這種關係可以用“新類是現有類的一種類型”這句話加以概括!所謂向上轉型,就是說,既然我們可以將子類看作是基類的一種類型,那麼我們就將一個基類引用指向一個子類對象,或者是將一個子類引用轉換成一個基類引用。這個過程,導出類轉換成基類的過程,在繼承圖上是向上移動的,因此我們稱之爲向上轉型,如下圖所示:

我們仔細理解一下這個轉換,從下到上的過程總是一個較爲專用類型轉換成一個較爲通用的類型,因此,這個轉換過程總是安全的,可以理解爲,子類是基類的一個超集,子類所含有的方法 大於等於 基類中的方法。

特別需要注意的一點:由於我們使用的是基類的引用指向子類的對象,在內存中,雖然我們確實在堆內存中是一個子類實例,但是對外暴露的引用確是一個基類引用,這樣的話,實際上,這個引用指向的是子類對象中包含的父類對象,也就是說,這個基類引用是不可以訪問子類新增加的成員的(屬性和方法)。這些將在多態進行講解。

最後,關於選擇組合和繼承關係,一個最清晰的判斷辦法就是在你的程序中是否需要從新類想基類進行項上轉型,如果需要,則繼承關係是必要的,如果不需要,則應該好好考慮一下是否真的需要繼承。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章