Java繼承、抽象、介面、多型
繼承
提取兩個類中的共性內容,生成一個父類,讓這連個類繼承這個父類
作用:
1)提高了程式碼的複用性
2)讓類與類之間產生了關係。有了這個關係,才有了多型的特性。
注意:千萬不要為了獲取其他類的功能,簡化程式碼而繼承。
必須是類與類直接有所屬關係才可以繼承,所屬關係is a。
Java語言中:Java只繼承單繼承,不支援多繼承。
因為多繼承容易帶來安全隱患:當多個父類中定義了相同功能時,如果這些功能的實現不同,那麼子類物件在呼叫這個功能時不確定要執行哪個方法。
但是Java保留了這種機制,並用另一種體現形式來完成表示,那就是實現多個介面。
Java支援多層繼承。也就是一個繼承體系。
如何使用一個繼承體系中的功能?
想要使用體系,先查閱體系父類的描述,因為父類中定義的是該體系中的共性功能,通過了解共性功能,就能瞭解該體系的基本功能,該體系也就基本可以使用了。
那麼在具體呼叫時,要建立最子類的物件,為什麼呢?
1)是因為有可能父類是不能建立子類的,比如抽象類
2)建立子類物件可以使用更多的功能,包括基本的也包括特有的
總而言之,查閱父類功能,建立子類物件使用功能
聚合關係:比如球隊由許多球員聚集在一起
組合關係:比如身體的各個部分組成了一個人
子類出現後,類成員的特點:
1.成員變數
如果子類中出現非私有的同名成員變數時
子類要訪問本類中的變數,用this。
子類要訪問父類中的同名比那裡,用super
super的使用和this的使用幾乎一致。
this代表的是本類物件的引用,super代表的是父類物件的引用。
2.子父類中的函式
當子類出現和父類一模一樣的函式時,當子類物件呼叫該函式,會執行子類函式的內容,如同如來的函式被覆蓋一樣。
這種情況是函式的另一個特性:重寫(覆蓋)
當子類繼承父類,沿襲了父類的功能到子類中,但是子類雖具備該功能,但是功能的內容卻和父類不一致,
這時,沒有必要定義新功能,而是使用覆蓋特殊,保留父類的功能定義,並重寫子類功能。
重寫方法注意事項:
1)子類覆蓋父類,必須保證子類許可權大於等於父類許可權,才可以覆蓋,否則編譯失敗
2)靜態只能覆蓋靜態
注意:
過載:只看同名函式的引數列表
重寫:子父類方法要一模一樣
3.子父類中的建構函式
在對子類物件進行初始化時,父類的建構函式也會執行,那是因為子類的建構函式預設第一行有一條隱式的語句super(),
super()會訪問父類中空引數的建構函式,而且子類中所有的建構函式預設第一行是super()。如果父類沒有空引數的建構函式,那麼子類建構函式中必須顯示呼叫父類的有參構造方法。
為什麼子類一定要訪問父類中的建構函式?
因為父類中的資料,子類可以直接獲取,所以子類物件在建立時,要檢視父類是怎樣對這些資料進行初始化的。
所以子類在物件初始化時,要先訪問一下父類中的建構函式。
如果要訪問父類中指定的建構函式,可以通過手動定義super語句的方式來指定。
子類的例項化過程
結論:
子類中所有的建構函式,預設都會訪問父類中空引數的建構函式。
因為子類的每一個建構函式內第一行都有一句隱式的super()。
當父類中沒有空引數的建構函式時,子類必須手動通過super語句形式來指定要訪問父類中的建構函式。
當然子類的建構函式第一行也可以手動指定this語句來訪問本類中的建構函式。
子類中至少會有一個建構函式會訪問父類的建構函式。
final關鍵字:最終,作為一個修飾符
1)可以修飾類、函式、變數
2)被final修飾的類不可以被繼承。為了避免繼承,被子類複寫功能
3)被final修飾的函式不可以被複寫
4)被final修飾的變數是一個常量只能賦值一次,既可以是成員變數,也可以是區域性變數
當在描述事物時,一些資料的出現值是固定的,那麼這時為了增強閱讀性,都給這些值起個名字,方便於閱讀。
而這個值不需要改變,所以加上final修飾符。
作為常量,常量的書寫規範是所有字母都有大寫,如果由多個單片語成,則單詞間通過_連線。
5)內部類定義在類中的區域性位置上時,只能訪問該區域性被final修飾的區域性變數
當多個類中出現相同功能,但是功能主體不同時,這時可以進行向上抽取,只抽取功能定義,而不抽取功能主體。
抽象:看不懂
抽象類的特點:
1)抽象方法一定定義在抽象類中,抽象類中可以有普通方法
2)抽象方法和抽象類都必須被abstract關鍵字修飾
3)抽象類中不可以用new建立物件,因為呼叫抽象方法沒意義
4)抽象類中的方法要被使用,必須由子類複寫所有的方法後,建立子類物件呼叫。
如果子類只覆蓋了部分抽象方法,那麼該子類還是一個抽象類。
抽象類和一般類沒有太大的區別
該如何描述事物,就如何描述事物,只不過,該事物出現一些不確定的部分,也是該事物的功能,需要明確出現,但是無法定義主體,可以通過抽象方法來表示。
抽象類比一般類多了抽象函式,並且抽象類不可以例項化。
注意:抽象類中可以不定義抽象方法,這樣做僅僅是為了不讓該類建立物件
練習:
需求:獲取一段程式執行的時間
原理:獲取程式開始和結束的時間並相減
程式碼實現:
public abstract class GetTime {
public final void getTime(){
long start = System.currentTimeMillis();
runCode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
public abstract void runCode();
}
class SubGetTime extends GetTime{
@Override
public void runCode() {
for(int i=0; i<1000; i++)
System.out.println(i);
}
public static void main(String[] args) {
SubGetTime sgt = new SubGetTime();
sgt.getTime();
}
}
這裡用到了模板方法設計模式
什麼是模板方法設計模式?
在定義功能時,功能的一部分是確定的,但是有一部分是不確定的,而確定的部分在使用不確定的部分,那麼這時就將不確定的部分暴露出去,由該類的子類去完成。
介面:初步理解,可以認為是一種特殊的抽象類
當抽象類的方法都是抽象的,那麼該類可以通過介面的形式來表示。
class用於定義類
interface用於定義介面
介面定義時,格式特點:
1)介面中常見定義:常量,抽象方法
2)介面中的成員都有固定修飾符
常量:public static final
方法:public abstract
注意:介面中的成員都是public的
介面:不可以建立物件,因為有抽象方法,需要被子類實現,子類對介面中的抽象方法全都覆蓋後,子類才可以例項化,否則子類是一個抽象類
介面可以被類多實現,也是對多繼承不支援的轉換形式
介面可以繼承多個介面,但是多個介面中如果有同名同參數列表的函式,那麼這兩個函式的返回值必須相同
當一個類實現了多個介面時,同理
介面屬於擴充套件功能,繼承是必須具備的基本功能
多型:可以理解為事物存在的多種體現形態
1)多型的體現
父類的引用指向自己的子類物件
父類的引用也可以接收自己的子類物件
2)多型的前提
必須是類與類直接有關係,要麼繼承,要麼實現
通常還有一個前提:存在覆蓋
3)多型的好處
多型的出現大大提高了程式的擴充套件性
4)多型的弊端
提高了擴充套件性,但是隻能使用父類的引用訪問父類中的成員
5)多型的應用
向上轉型
Animal animal = new Cat();
向下轉型
if(animal instanceof Cat)
Cat cat = (Cat)animal;
不能將父類物件轉成子類型別,我們能轉換的是父類引用指向了自己的子類物件時,
該引用可以被提升,也可以被強制轉換。強轉時只能轉成相應的子類物件,而不是其他子類物件。多型自始自終都是子類物件在做著變化。
在多型非靜態成員函式中的特點:
在編譯時期:參閱引用型變數所屬的類中是否有呼叫的方法。如果有,編譯通過,如果沒有,編譯失敗。
在執行時期:參閱物件所屬的類中是否有呼叫的方法
總而言之:編譯看左邊,執行看右邊
在多型中,成員變數的特點:
無論編譯和執行都看左邊(父類)
在多型中,靜態成員函式的特點:
無論編譯和執行,都是參考左邊
Object:是所有物件的直接或者間接父類,該類中定義的肯定是所有物件都具備的基本功能。
Object類中已經提供了對物件是否相同的比較方法。
如果自定義類中也有比較相同的功能,沒有必要重新定義。
只要沿襲父類中的功能,建立自己特有比較內容即可,這就是覆蓋。