1. 程式人生 > 實用技巧 >第八章、面向物件高階

第八章、面向物件高階

第八章、面向物件高階

1. 多型

1.1 基本概念

多型就是指同一種事物表現出來的多種形態。
 
飲料:可樂、雪碧、紅牛、美年達、.............
寵物:狗、貓、鳥、..........
整數: byte b = 10;    short s = 10;  int i = 10;.......
 
 
C++多型分為兩種:編譯多型、執行時多型
 
 
Java中的多型只有一種,就是執行時多型。是一種執行期間的行為,而不是編譯期的行為。

1.2 語法格式

父類型別   引用 = new  子類型別();
 
如:
    Person p = new Student();
    p.show();
 
 
解析:
    在編譯階段p是Person型別,因此呼叫Person類自己的show()方法,若沒有則編譯的時候就報錯。
    在執行階段p真正指向的物件是Student型別,因此最終呼叫的是Student類中自己的show()方法。
 
 
當使用多型方式呼叫方法的時候,首先會檢查父類中是否有該方法,如果沒有,則編譯報錯。
如果有,再去呼叫子類的同名方法。(注意:靜態static方法屬於特殊情況,靜態方法只能繼承,不能重寫override,如果
子類中定義了同名同形式的靜態方法,它對父類方法只能起到隱藏的作用。呼叫的時候用誰的引用,則呼叫誰的版本。)      
 
 
課堂小案例:
    自定義Person類實現封裝,特徵由:姓名和年齡
    自定義Student類繼承自Person類實現封裝,特徵有:學號。
    自定義TestPersonStudent類,在main()方法中使用Person類的引用指向Student類的物件。
 
 
多型存在的三個比較條件:
    1.要有繼承
    2.要有重寫
    3.父類引用指向子類物件

1.3 多型效果

1.對於指向子類物件的父類來說,在編譯期間只能呼叫父類的方法,不能直接呼叫子類的方法。
2.對於父子類都有的非靜態方法來說,最終呼叫子類中的重寫版本。
3.對於父子類中都有的靜態方法來說 ,最終呼叫父類中的版本,與指向的物件型別無關。

1.4 引用型別之間的轉換

基本概述
    1.引用型別之間的轉換必須發生在父子類之間,分為自動型別轉換(向上轉換)和強制型別轉換(向下轉換)
 
 
    2.向上轉換(子類型別向父類型別的轉換)
        父類引用指向子類物件: Person p = new Student();
        把一個子類物件轉換為父類物件,向上轉型(自動轉型、隱式轉型),程式碼中是父類引用指向子類的物件,父類
        引用指向型別例項,可以呼叫子類重寫父類的方法以及父類派生的方法,無法呼叫子類獨有的方法。
 
        注意:
            父類中的靜態方法無法被子類重寫,所以向上轉型之後只能呼叫父類原有的靜態方法。
 
 
    3.向下轉換(父類型別向子類型別的轉換)
        子類引用指向父類例項,必須進行強制型別轉換,可以呼叫子類特有的方法。必須滿足轉型的條件才能。
 
        instanceof 運算子可以進行判斷,左邊的物件是否是他右邊物件的例項,換句話來說就是左側物件是否滿足右側物件型別
        的特徵如果是返回true
 
        if(p instanceof Teacher){}
 
        父類中的靜態方法(含有static修飾的方法,只能被子類繼承使用無法被子類重寫)。
 
 
    4.當沒有父子類關係發生強制型別轉換則編譯報錯,當擁有父子類關係發生強制轉換是編譯通過,
        若目標型別並不是該引用真正指向的型別時,在執行階段發生型別轉換異常。
 
    5.為了避免上述錯誤的發生,通常只要進行強制型別轉換就需要進行判斷,格式如下:
            if(引用變數名  instanceof 資料型別){}
            - 判斷引用指向的物件是否為指定的資料型別,若是則返回true,否則返回false;

1.5 多型的實際意義

多型的意義在於:可以遮蔽不同子類的差異性編寫通用的程式碼,從而產生不同的效果。
 
 
案例:
    形狀:橫座標、縱座標
    矩形:長度、寬度
    圓形:半徑
 
    寫一個方法要求既能列印矩形又能列印圓形。

2. 抽象類

2.1 抽象方法

抽象方法就是指不能被具體實現的方法,也就是沒有方法體,並且使用abstract關鍵字修飾;
 
語法格式:
    訪問修飾符   abstract  void  show();

2.2 抽象類

抽象類就是指使用abstract關鍵字修飾的類,抽象類不能被例項化物件

2.3 注意事項

1.抽象類中可以有成員變數、成員方法以及構造方法;
2.抽象類中可以有抽象方法也可以沒有;
3.擁有抽象方法的類必須是抽象類,因此通常情況下認為擁有抽象方法並且有abstract
    關鍵字修飾的類才認為是真正的抽象類;

2.4 實際意義

抽象類的意義不再與例項化物件而在於被繼承,若一個類繼承自抽象類則必須重寫抽象方法,否則該類也得變成抽象類。
因此,抽象類對子類具有強制性和規範性,叫做模板設計模式。
 
經驗分享:
    在以後的開發中推薦使用父類引用指向子類的物件形式,因為父類引用直接呼叫的方法一定是
父類擁有的方法,當需要更換指向子類物件的時候,只需要將new後面的改方式型別更改一下就可以了,其他的程式碼無需改動,因此提高了程式碼的可維護性和可擴充套件性。
 
    該方式的缺點在於:父類引用不能直接呼叫子類獨有的方法,若呼叫則需要強制型別轉換。
 
 
 
練習:
    自定義Account類實現封裝,特徵:賬戶餘額,提供計算利息並返回的抽象方法。
 
    自定義FixedAccount類繼承Account類,實現抽象方法的重寫。
 
    自定義TestFixedAccount類,在main()方法中使用多型的語法建立物件並呼叫計算利息的方法。

2.5 抽象總結

抽象關鍵字  abstract    ,當abstract修飾類,就是抽象類,抽象類不能被例項化,
當 abstract 修飾方法抽象方法沒有方法體,繼承抽象類,必然要重寫抽象方法。

3. 介面

3.1 基本概述

介面就是一種比抽象類還抽象的類,該型別不能例項化物件。
 
定義類的關鍵字是class   而定義介面的關鍵字是interface
 
繼承類的關鍵字是extends,而實現介面的關鍵字是implements
 
當多個型別之間具有相同的行為能力的時候,java中就可以通過介面來進行型別之間的聯絡。
通過介面可以解決java中單繼承所帶來的一些型別無法共享的問題。
 
介面定義了某一些規範,並且需要去遵守
介面不關心類的內部資料,也不關心這些類裡方法的實現細節,它只規定這些類裡必須提供的某些方法。

3.2 語法格式

修飾符  interface  介面名稱 [extends 父介面1,父介面2,......] {
    零個到多個常量的定義.........
    零個到多個抽象方法的定義......
    零個到多個預設方法定義.......(jdk1.8新特性)
    零個到多個靜態方法的定義.......(jdk1.8新特性)
}

3.3 注意事項

1.介面可以實現多繼承,也就是說一個介面可以同時繼承多個父介面。
2.實現介面的類如果不能實現所有介面中待重寫的方法,則必須設定為抽象類。
3.一個類可以繼承自一個父類,同時實現多個介面。
4.介面中的所有成員變數都必須有public static final共同修飾,也就是常量。
5.介面中所有成員方法必須有public abstract共同修飾,也就是抽象方法。

4. 類和介面

4.1 類和介面之間的關係

類和類之間採用繼承的關係        使用extends關鍵         支援單繼承
類與介面之間採用實現的關係       使用implements關鍵字     支援多實現
介面與介面之間採用繼承的關係  使用extends關鍵字            通常認為支援單繼承

4.2 抽象類和介面的主要區別

1.定義抽象類的關鍵是class,而定義介面的關鍵字是interface。
2.繼承抽象類關鍵字extends,而實現介面關鍵字 implements
3.繼承抽象類支援單繼承,而實現介面支援多實現。
4.抽象類有構造方法,但是介面沒有
5.介面中的所有的成員變數都必須是常量,而抽象類中可以是變數。
6.介面中的所有成員方法都必須是抽象方法,而抽象類中可以是普通方法
7.介面中增加方法一定影響子類,而抽象類中可以不影響。

4.3 介面中的常量為什麼必須使用public static final修飾

public 作用修飾符公開的,也就是說介面的實現類可以使用這個變數。
 
static 修飾就是表示類的,隨著類的載入而存在的
    如果不是static的話,就表示屬於物件的,只有建立物件才能有它,而介面是不能建立物件的,所以介面的常量定義必須為static。
 
final 修飾就是儲存介面定義的常量不能被實現類去修改,如果沒有final的話,由子類隨意修改,介面建立常量將沒有意義了。

5. 內部類(瞭解)

5.1 基本概述

當一個類的定義放在另一個類的實體時,則該類叫做內部類,該類所在的類叫做外部類。
在一個類體中可以出現的內容:成員變數、成員方法、構造方法、構造塊、靜態語句塊、靜態變數、方法、內部類。
 
巢狀類:
    內部類(成員內部類、區域性內部類、匿名內部類) 
    靜態巢狀類

5.2 語法格式

class 外部類類名{
    class 內部類類名{
        內部類的類體;
    }
 
}

5.3 成員內部類

成員內部類定義在另一個類或介面中的內部類
 
 
注意事項:
    1.必須先建立外部類物件才能建立成員內部類物件
    2.不能含有靜態變數、靜態程式碼塊、靜態方法(除了靜態常量)
    3.外部類可通過成員內部類的物件呼叫內部類私有成員
    4.成員內部類是一個獨立的類,編譯成獨立的.class檔案
 
作用:
    成員內部類既可以訪問外部類資訊,又可以訪問父類資訊,從而使多繼承的解決方案變得完整。

5.4 區域性內部類

區域性內部類是定義在方法或程式碼塊裡的內部類

注意事項:
    1.不能含有靜態變數、靜態程式碼塊、靜態方法
    2.只能在定義該類的方法或程式碼塊中使用,必須在使用前定義
    3.防偽碼它所有方法的區域性變數的時候,區域性變數必須是有效的常量。
    4.是一個獨立的類,編譯成獨立的.class檔案   
    5.只能使用abstract、final修飾
    6.定義靜態塊或方法時候,只能訪問外部類的靜態成員。

5.5 匿名內部類

匿名內部類是直接使用介面或父類例項化物件時建立沒有名字的內部類。
 
語法格式:
    介面/父類型別  引用名 = new 介面/父類型別(){
        進行方法的重寫;
    };

注意事項:
    1.必須且僅能繼承一個父類或實現一個介面
    2.沒有class關鍵字,沒有類名
    3.是特殊的區域性內部類
    4.僅能使用一次
    5.不能定義構造方法
    6.匿名類不能是抽象類

優點以及作用:
    匿名內部類可以使我們的程式碼更加緊湊,簡潔、封裝性比較好
    匿名內部類可以靈活的建立
    匿名內部類使用多繼承的解決方案比較完整

5.6 靜態巢狀類

靜態巢狀類定義在另一個類、介面,使用static關鍵字修飾的巢狀類
 
 
注意事項:
    1.不需要通過生成外部類物件來生成靜態巢狀類物件
    2.只能直接訪問外部類的靜態成員
    3.外部類可通過靜態巢狀類的物件呼叫內部類的成員
    4.可以定義靜態成員變數或靜態成員方法。

5.7 靜態巢狀類和非靜態巢狀類的區別

名稱              內部類(非靜態巢狀類)                靜態巢狀類
 
定義位置上          成員位置、方法、程式碼塊            只能在外部類的成員位置
組成               例項成員、靜態常量、構造方法       例項成員、靜態成員、靜態程式碼塊、構造方法
物件建立            必須先有外部類物件               不依賴於外部類例項,可以直接例項化
訪問外部類          可以直接訪問外部類所有成員         只能直接訪問外部類的靜態成員