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