一、繼承
- 概述
繼承是面向對象的重要特征之一,當多個類中存在相同的屬性和行為時,將這些內容抽取到單獨一個類中,那多個類中無需再定義這些屬性和行為,只需繼承那個單獨的類即可。單獨的類稱為父類或超類
多個類稱為子類
例如:貓和老虎都是屬于貓科動物。
描述貓、老虎這些對象所創建的類,就是子類;
而描述貓科動物這個對象所創建的類,就是父類。
類與類之間存在了繼承關系,子類可以直接訪問父類中非私有的屬性和行為,代碼中通過 extends 關鍵字來表示繼承關系。例如:class Son extends Father{}//代碼中的書寫格式
注:千萬不能為了獲取其他類中的功能,簡化代碼而去繼承。
必須是類與類之間有所屬關系才能繼承。
這種所屬關系的表示為 is a
- 特點
- 提高了代碼的復用性。2. 讓類與類之間產生了關系,是多態的前提。
注:Java 語言中只支持單繼承,不支持多繼承。
Java 雖然不支持多繼承,但是保留了類似的機制,并且以另一種形式來體現,也就是多實現。例如:一個兒子只能有一個父親。
只能單繼承的原因?因為類與類多繼承的話,容易產生安全隱患。例如:當多個父類中定義了相同的功能,但功能的內容不相同時,子類對象就無法確定要運行哪一個父類的功能了。
- 應用
Java 中雖然不支持多繼承,但是支持多層繼承,也就是繼承體系。例如:兒子繼承父親,父親繼承爺爺等。class A{}
class B extends A{}
class C extends B{}
如何使用繼承體系中的功能?想要使用繼承體系,先要查詢繼承體系中父類的描述,因為父類中定義的是該繼承體系中的共性功能,了解共性功能后,就可以知道該體系的基本功能,這個繼承體系就基本可以使用了。
具體調用時,需要先創建最子類的對象,原因是?有可能父類不能創建對象;
創建子類對象可以使用更多的功能,包括基本的父類共性功能,也包括子類的特有功能。總結:查閱父類功能,創建子類對象使用功能。
例如:
子父類出現后,類成員(變量、函數、構造函數)的特點:變量如果子類中出現非私有的同名成員變量時,子類要訪問本類中的變量,用 this;子類要訪問父類父類中的同名變量,用 super。
this 和 super 的使用幾乎一致,并且兩者都存于方法區中。this 表示本類對象的引用
super 表示父類對象的引用
函數 —— 重寫(覆蓋)當子類出現和父類一模一樣的函數時,子類對象調用該函數,會運行子類的函數內容,相當于父類的函數被覆蓋了,這就是函數的一個特性:重寫(覆蓋)。
當子類繼承父類,并且沿襲了父類的功能時,子類雖具備父類的該功能,但是子類中功能的內容卻和父類不一致,這時,無需重新定義新功能,而是使用重寫(覆蓋)這一特性,保留父類的功能,并重寫子類的功能內容,子類中同時想具有父類函數中的內容時,可以使用 super.() 方法。
在上面例子中的 Student 類中重寫 sleep() 方法,如下:
注:
- 靜態只能覆蓋靜態。2. 父類中的私有方法不能被重寫(覆蓋)。3. 子類重寫(覆蓋)父類,必須保證子類權限大于或等于父類權限,才可以重寫(覆蓋),否則會導致編譯失敗。
重寫(覆蓋)與重載的區別:重寫(覆蓋):子父類方法要一模一樣。
重載:只看同名函數的參數列表。
構造函數對子類對象進行初始化時,父類的構造函數也會運行,因為子類的每一個構造函數默認第一行都有一條隱式的語句 super();
super():訪問父類中空參數的構造函數,而且子類中所有構造函數默認第一行都是 super();
子類為什么一定會訪問父類的構造函數?父類中的數據子類可以直接獲取,子類對象在建立時,需先查看父類中是如何對這些數據進行初始化的,因此子類在對象初始化時,需要先訪問父類中的構造函數。
為什么 super() 和 this() 不能在同一個構造函數中?因為不能在同一行。
為什么 super() 和 this() 不能在同一行?因為需要先做初始化動作,在子類構造函數中必須有一個 super 語句或 this 語句。
注:super 語句一定是定義在子類構造函數中的第一行。
如果要訪問父類中指定的構造函數,可以通過手動定義 super 語句的方式去指定。
總結:
- 子類中所有構造函數,默認都會訪問父類的空參數構造函數;2. 子類中每一個構造函數中的第一行都有一句隱式 super(); 語句;3. 父類沒有空參數構造函數時,子類必須手動定義 super 語句或 this 語句形式去指定要訪問的構造函數;4. 子類的構造函數第一行也可以手動指定 this 語句去訪問本類中的其它構造函數;5. 子類中至少會有一個構造函數會訪問父類中的構造函數。
final 關鍵字由于繼承的出現,打破了對象的封裝性,使得子類可以隨意重寫(覆蓋)父類的功能,這也是繼承的弊端。為了解決繼承的這個弊端,便出現了 final(最終)關鍵字。
final 修飾符的特點:
- 可以修飾類、函數、變量。2. final 修飾的類不能被繼承。(可以避免被繼承、被子類重寫父類函數功能)3. final 修飾的方法不能被重寫。4. final 修飾的變量是常量,并且只能賦值一次,可以修飾成員變量,也可以修飾局部變量。5. 內部類定義在類中的局部位置時,只能訪問該局部被 final 修飾的局部變量。
二、抽象
- 概述
從多個事物中將共性的、本質的內容抽取出來,這就是抽象。例如:狗和狼共性都是犬科,犬科就是抽象出來的概念。
Java 中可以定義沒有方法體的方法,方法的具體實現由子類實現,多個對象如果具有相同功能,但功能的具體內容有所不同,那么在抽取過程中,只抽取功能定義,未抽取功能主體內容,這樣只有功能聲明,沒有功能主體的方法,這樣的方法稱為抽象方法,而含有抽象方法的類稱為抽象類。例如:狗和狼都有吼叫的方法,但是吼叫的內容不一樣,因此抽象出來的犬科雖然有吼叫功能,但是并沒有明確吼叫的實現內容細節。
格式:abstract 類名 {}
子類名 extends 抽象類名 {}
- 特點
抽象類和抽象方法必須使用 abstract 關鍵字進行修飾。
抽象方法只有方法聲明,沒有方法體,定義在抽象類中。格式:修飾符 abstract 返回值類型 函數名(參數列表);
抽象類不能被實例化,也就是說不能用 new 創建對象,抽象類是從具體事物抽取出來的,抽象類本身是不具體的,沒有對應的實例。例如:犬科是一個抽象的概念,真正存在的實例是狗和狼。
抽象類通過其子類進行實例化,子類必須重寫(覆蓋)抽象類中所有的抽象方法,子類才能實例化,否則該子類也會是抽象類。
注:抽象類中可以存在非抽象方法。
- 抽象類與一般類
區別:抽象類不可以實例化。
抽象類不能創建對象,但是也有構造函數,提供給子類實例化調用。
抽象類比一般類多了抽象方法,抽象類中可以定義抽象方法,也可以定義非抽象方法。
注:抽象類中可以不定義抽象方法,使抽象類不被實例化,常用于模塊設計。
被 abstract 修飾的函數不能同時被 private、static、final 修飾。private:抽象類中的抽象方法需要被子類重寫,而 private 修飾的方法是私有的,不被子類所知,無法重寫。
static:static 修飾的方法可以直接使用類名調用,不需要使用對象調用,如果 static 使用在抽象方法上就沒有運行的意義了。
final:final 修飾的類不能有子類,而 abstract 修飾的類一定是一個父類。
三、接口
- 概述
當抽象類中所有的方法都是抽象方法時,該類可以通過接口的形式表示,使用 interface 來表示,子類中使用 implements 來實現,接口可以認為是特殊的抽象類。
格式:interface 接口名{}
子類名 implements 接口名 {}
特點:接口中常見定義:常量、抽象方法。
接口中成員都是使用 public 修飾的。
接口中成員都有固定的修飾符:常量:public static final
方法:public abstract
注:接口中的常量可以不寫 public static final,方法可以不寫 public abstract,編譯時會自動添加這些修飾符,這是 Java 中接口固定的修飾符,為了方便閱讀,一般都會寫上。
特點
接口是程序功能的擴展。
接口是對外暴露的規則。
接口的出現降低了耦合性。
接口可以用來多實現,對于 Java 不支持多繼承的缺陷而做的轉換,Java 支持多實現。
類與接口之間是實現關系,類只能繼承一個類,但同時可以實現多個接口。
接口與接口之間可以有繼承關系,而且接口與接口之間支持多繼承。
注: 1. 接口不可以創建對象,因為接口有抽象方法需要子類實現(implements),子類需要全部重寫接口中的抽象方法后,子類才能實例化,否則該子類也會是抽象類。 2. 實現多個接口時,接口中不允許有返回類型不同的同名抽象函數,如果有這樣的情況時,子類實現將無法重寫接口的抽象方法。接口與抽象類
相同點:都是不斷向上抽取出來的抽象概念。
區別:接口是實現,是 "like a" 關系;
抽象類是繼承,是 "is a" 關系。
接口中的常量和方法都是 public 修飾的權限;
抽象類中可以有私有變量或方法。
接口體現的是實現關系,可以多實現,接口與接口之間可以有繼承關系;
抽象類體現的是繼承關系,只能單繼承。
接口中所有方法都是抽象方法,接口中的成員都是有固定的修飾符;
抽象類中可以定義非抽象方法,提供給子類直接使用。