標籤:多態 java編程思想
繼承,封裝,多態是物件導向程式設計中的三大特性.其實從某種程度上講繼承和封裝都是在為多態做準備,下面我們來瞭解多態這個重要的概念及其實現原理.
一.什麼是多態
所謂的多態就是指允許不同類的對象對同一訊息做出響應。即同一訊息可以根據發送對象的不同而採用多種不同的行為方式。(發送訊息就是函數調用).
我們知道java支援向上轉型,所以對於從同一個基類繼承而來的子類,我們都可以把其對象當成基類對象來處理.所以多態 就是指調用基類的某些方法,依據傳入的子類對象的不同而表現出不同的特性.(類似於現實中我們在不同的軟體下,按下相同的快速鍵會有不同的反應).下面的代碼示範了多態性:Bicycle Tricycle Unicycle都是Cycle類的子類,然後這四個類中都有wheels方法用於返回其輪子的數量.RideTest類中的wheels方法將所有傳入的對象都看成Cycle對象,但是當我們向裡面傳入Bicycle,Tricycle,Unicycle對象時是可以得到正確結果的,這就是多態.
package lkl;public class Cycle { public int wheels (){ return 0; }}public class Tricycle extends Cycle{ public int wheels(){ return 3; }}public class BiCycle extends Cycle{ public int wheels(){ return 2; }}public class Unicycle extends Cycle{ public int wheels(){ return 1; }}public class RideTest { public int wheels(Cycle cy){ return cy.wheels(); } public static void main(String[] args){ RideTest rt = new RideTest(); ///根據傳入的不同子類表現不同的行為,稱為多態 System.out.println(rt.wheels(new Unicycle())); System.out.println(rt.wheels(new BiCycle())); System.out.println(rt.wheels(new Tricycle()));
二.多態的實現原理
多態實現依賴於兩個原理:1.向上轉型,2.後期綁定.
java提供的向上轉型允許我們將子類的對象當成基類對象處理,這種轉型是多態的必要條件因為多態要求我們將所有的子類對象當成基類對象處理.向上轉型的合理性在於所有基類有的介面,子類也必定會有,這樣轉型後就不會發生調用子類不存在介面的錯誤.
我們知道對於一般的執行個體方法,都應該有一個調用它的對象,所以編譯器要將一個方法的調用和一個實體(對象)聯絡起來(綁定).如果在程式執行前就進行綁定,那麼稱為前期綁定.但對於多態來說,我們只有在運行時才知道到底是那個對象調用的方法,所以前期判定是行不通的.java引入了後期綁定(也叫動態綁定),它允許我們在程式運行時根據對象的類型進行綁定(這要求能夠在運行時判定對象的類型).這也是實現多態的核心所在.在C++中我們用virtual關鍵字表明一個函數能夠被其子類重寫,存在多態性.對應到java中,除了private static 修飾的函數,其餘函數都預設為後期綁定的,即存在多態性的可能.
三.陷阱
首先並不是所有的函數都具有多態性,像上面說的用private 或static修飾的函數是不存在多態性的,因為多態性的前提是繼承和重寫,基類介面需要對子類可見.private修飾的函數不能被子類顯示調用,而static函數是屬於類所有的,也可以看成不能被繼承的函數.所以這兩類函數不具備多態性.如果我們依據多態的概念來看,似乎基類中非private的成員變數也會具有多態性,但是實際上並沒有,這時java的規定.下面的代碼顯示了這三種情況:
package lkl;public class BaseTest { public int i=1; public int getFiled(){ return i; } private void privateTest(){ System.out.println("BaseTest.privateTest()"); } ///static函數 public static void staticTest(){ System.out.println("BaseTest.staticTest()"); }}package lkl;public class Test1 extends BaseTest{ public int i=9; public int getFiled(){ return i; } ///試圖重寫基類的privateTest() private void privateTest(){ System.out.println("Test1.privateTest()"); } ///試圖重寫基類的staticTest() ///@Override 添加Override出錯,說明不能重寫 public static void staticTest(){ System.out.println("Test1.staticTest"); } public static void main(String[] args){ BaseTest bt = new Test1(); ///域是不存在多態的 System.out.println(bt.i); System.out.println(bt.getFiled()); bt.f(); ///staticTest的調用不會產生多態性 bt.staticTest(); }}
四.一道習題
建立一個包含兩個方法的基類.在第一個方法中可以調用第二個方法,然後產生一個繼承自該基類的匯出類,並且覆蓋基類中的第二個方法.為該匯出類建立一個對象,將它向上轉型到基類類型並調用第一個方法,解釋發生的情況.
public class Test1 extends BaseTest{ @Override///重寫second方法 public void second(){ //super.g(); System.out.println("Test1.second()"); } public static void main(String[] args){ BaseTest bt = new Test1(); bt.first(); }}public class BaseTest { public void first(){ System.out.println("Base.first()"); second(); } public void second(){ System.out.println("Base.second()"); }}
結果為:
Base.first()
Test1.second()
這種情況的原因在於我們調用first這個方法的對象是子類對象,但是因為first方法沒有被子類重寫,所以沒有表現出多態;
我們在first中調用second函數的對象也是子類對象,因為second函數在子類中重寫了,所以表現出了多態性.
這個題目說明重寫也是多態的必要條件.
Thinking in Java---多態初探