物件導向之多型
例子
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、為什麼需要強轉
我們本來今天就是為了使用父類引用子類的物件去操作,為什麼還需要進行強轉回去呢
我們發現,使用多型沒有辦法呼叫子類獨有的方法,當我們需要呼叫子類獨有的方法的時候,我們進行強轉然後再呼叫
五、物件導向小結
物件導向的三大特性:封裝、繼承、多型
-
封裝是隱藏物件的屬性和實現細節
- 將類的成員屬性宣告為私有的,同時提供公有的方法實現對該成員屬性的存取操作
-
繼承是軟體可重用性的一種表現
- 新類可以在不增加自身程式碼的情況下,通過從現有的類中繼承其屬性和方法充實自身內容
-
多型是具有表現多種形態的能力的特徵
- 在程式設計的術語中,意味著一個特定型別的變數可以引用不同型別的物件,自動地呼叫引用的物件的方法
- 即根據作用到的不同物件型別,響應不同的操作
- 在程式設計的術語中,意味著一個特定型別的變數可以引用不同型別的物件,自動地呼叫引用的物件的方法