JAVA物件導向之多型

樹大有枯枝發表於2021-12-17

物件導向之多型

例子

class Person{}

class Boy extends Person{}

class Girl extends Person{}

一、多型的定義

生活上:

  • 通俗的講,就是同一個東西表現出多種狀態
  • 比如我開頭的例子,男孩,女孩都是人類。是人類的不同狀態

程式上:

  • 父類引用指向子類的例項
  • 同一個引用型別,使用不同的例項而執行不同操作
  • 當我們使用父類的引用,指向子類的例項的時候,實際上就是一個向上轉型的過程

我們一般在建立物件的時候

Boy boy = new Boy();

Girl girl = new Girl();

而多型的建立

//父類的引用指向子類的物件
//前面是父類宣告 = 新建一個子類的物件
Person p = new Boy();

我們可以直接輸出這個父類看一下

Person p = new Boy();
System.out.println(p);

執行結果:

Boy@2503dbd3

發現我們執行的時候識別到了它是Boy類

也就是說我們在編譯前它被識別為父類,而編譯後也就是執行的時候就被識別為子類了

這就是多型

編譯的時候建立父類引用指向子類的物件

而特性是執行的時候就已經是子類的物件了

二、為什麼使用多型

我們來看這樣一個場景

我們人都是要吃飯的,都有吃飯這個行為

class Person {
    public void eat(){
        System.out.println("是人就得吃飯");
    }
}

雖然人都得吃飯,但是針對到男孩,女孩在吃飯上是有一定的差異的。

就需要重寫一下父類的方法了

//男孩 
class Boy extends Person{
    public void eat(){
        super.eat();
        System.out.println("男孩要力氣,得吃肉");
    }
}
//女孩
class Girl extends Person{
     public void eat() {
         super.eat();
         System.out.println("女孩要苗條,得少吃");
     }
 }

我們假設一個食堂類,食堂裡是男孩,女孩吃飯的地方

class Canteen{
    public void meal(Boy boy){
        System.out.println("到食堂乾飯");
        boy.eat();
    }

    public void meal(Girl girl){
        System.out.println("到食堂乾飯");
        girl.eat();
    }
}

那麼我們想要男孩,女孩都能進去吃飯,我們就需要進行過載,才能識別所有的引數

測試程式碼:

Canteen c = new Canteen();
Boy boy = new Boy();
c.meal(boy);

但是這樣就會有一個問題

我們這裡是只有男孩和女孩這兩個類,過載了一次,程式碼開始有些重複了。

我們之前說過程式碼重複,我們就需要找到合適的方法解決重複的程式碼

為什麼使用多型

  • 為了解決重複程式碼
  • 為了完善專案結構

三、如何實現多型

我們一開始說過,父類引用指向子類的物件是多型,該如何運用呢

Person p = new boy();
p.eat();

我們發現呼叫的是子類重寫父類的方法

實際上多型的使用就這麼簡單

  • 系統會自動進行型別轉換

  • 通過父類引用變數呼叫的方法是子類覆蓋或繼承的子類方法,不是父類的方法

  • 通過父類引用變數無法呼叫子類特有的方法

1、通過方法引數

將我們引數從子類改為父類

程式碼如下

class Canteen{
    public void meal(Person person){
        System.out.println("到食堂乾飯");
        boy.eat();
    }
}

我們測試傳參,測試程式碼不需要更改

Canteen c = new Canteen();
Boy boy = new Boy();
c.meal(boy);
//測試的引數不需要改變

也是能夠實現的,但是不需要過載

這個能夠實現是因為形參Person person就是建立了一個父類的引用

當我們傳參的時候,就是使父類指向了子類的物件

2、通過方法的返回值

這個就更簡單了

我們先寫一個方法

程式碼如下

public Person getPerson(String choose){
    if(choose.equals("boy")){
        return new Boy();
    }else if(choose.equals("girl")){
        return new Girl();
    }else{
        System.out.println("輸入有誤");
        return null;
    }
}

當我們呼叫該方法的時候,通過引數選擇,返回男孩或者女孩。

返回的都是一個Person型別的,但是卻是一個父類的引用指向子類的物件

四、注意點

1、轉型問題

剛才說過,父類引用指向子類的物件,是向上轉型。

//左邊是父類  右邊是子類
//和以前學習基礎型別時候的預設強轉很像
//int a = short
Person p = new Boy();

有向上轉型就有想下轉向

//向下轉型的注意點是:能被轉為子類的父類,一定是父類引用指向子類的物件
//能轉成功,也必須是父類指向的就是這個子類才行
//同樣和基礎型別的強轉很像
//short a = (short)int
Boy b =(Boy)p;

2、確保轉型成功--instanceof關鍵字

我們是已知當前這個物件是父類指向的某個子類

所以直接能夠將父類向下轉型為子類

但是我們可以看下面程式碼

Person p = new Boy();
Girl g = (girl)p;

執行之後就告訴我們,是沒辦法將Boy類轉為Girl類

我們明顯的發現一定需要轉為對應的子類才能成功。

所以我們需要一些判斷手段

boolean a = p instanceof Boy;
//我們發現返回的是true

由此我們可以看出來

[物件名] instanceof [類];
//判斷物件是否是該類的
//是返回true 否返回false

這樣,我們就可以直接先判斷再強轉了

if(p instanceof Boy){
    Boy b =(Boy)p;
}

可保萬無一失

3、為什麼需要強轉

我們本來今天就是為了使用父類引用子類的物件去操作,為什麼還需要進行強轉回去呢

我們發現,使用多型沒有辦法呼叫子類獨有的方法,當我們需要呼叫子類獨有的方法的時候,我們進行強轉然後再呼叫

五、物件導向小結

物件導向的三大特性:封裝、繼承、多型

  • 封裝是隱藏物件的屬性和實現細節

    • 將類的成員屬性宣告為私有的,同時提供公有的方法實現對該成員屬性的存取操作
  • 繼承是軟體可重用性的一種表現

    • 新類可以在不增加自身程式碼的情況下,通過從現有的類中繼承其屬性和方法充實自身內容
  • 多型是具有表現多種形態的能力的特徵

    • 在程式設計的術語中,意味著一個特定型別的變數可以引用不同型別的物件,自動地呼叫引用的物件的方法
      • 即根據作用到的不同物件型別,響應不同的操作

image

相關文章