好東西分享
什么是多態?它的實現機制是什么呢?重載和重寫的區別在那里?這就是這一次我們要回顧的四個十分重要的概念:繼承、多態、重載和重寫。
繼承(inheritance)
簡單的說,繼承就是在一個現有類型的基礎上,通過增加新的方法或者重定義已有方法(下面會講到,這種方式叫重寫)的方式,產生一個新的類型。繼承是面向對象的三個基本特征--封裝、繼承、多態的其中之一,我們在使用JAVA時編寫的每一個類都是在繼承,因為在JAVA語言中,java.lang.Object類是所有類最根本的基類(或者叫父類、超類),如果我們新定義的一個類沒有明確地指定繼承自哪個基類,那么JAVA就會默認為它是繼承自Object類的。
我們可以把JAVA中的類分為以下三種:
- 類:使用class定義且不含有抽象方法的類。
- 抽象類:使用abstract class定義的類,它可以含有,也可以不含有抽象方法。
- 接口:使用interface定義的類。
在這三種類型之間存在下面的繼承規律:
- 類可以繼承(extends)類,可以繼承(extends)抽象類,可以繼承(implements)接口。
- 抽象類可以繼承(extends)類,可以繼承(extends)抽象類,可以繼承(implements)接口。
- 接口只能繼承(extends)接口。
請注意上面三條規律中每種繼承情況下使用的不同的關鍵字extends和implements,它們是不可以隨意替換的。大家知道,一個普通類繼承一個接口后,必須實現這個接口中定義的所有方法,否則就只能被定義為抽象類。我在這里之所以沒有對implements關鍵字使用“實現”這種說法是因為從概念上來說它也是表示一種繼承關系,而且對于抽象類implements接口的情況下,它并不是一定要實現這個接口定義的任何方法,因此使用繼承的說法更為合理一些。
以上三條規律同時遵守下面這些約束:
- 類和抽象類都只能最多繼承一個類,或者最多繼承一個抽象類,并且這兩種情況是互斥的,也就是說它們要么繼承一個類,要么繼承一個抽象類。
- 類、抽象類和接口在繼承接口時,不受數量的約束,理論上可以繼承無限多個接口。當然,對于類來說,它必須實現它所繼承的所有接口中定義的全部方法。
- 抽象類繼承抽象類,或者實現接口時,可以部分、全部或者完全不實現父類抽象類的抽象(abstract)方法,或者父類接口中定義的接口。
- 類繼承抽象類,或者實現接口時,必須全部實現父類抽象類的全部抽象(abstract)方法,或者父類接口中定義的全部接口。
繼承給我們的編程帶來的好處就是對原有類的復用(重用)。就像模塊的復用一樣,類的復用可以提高我們的開發效率,實際上,模塊的復用是大量類的復用疊加后的效果。除了繼承之外,我們還可以使用組合的方式來復用類。所謂組合就是把原有類定義為新類的一個屬性,通過在新類中調用原有類的方法來實現復用。如果新定義的類型與原有類型之間不存在被包含的關系,也就是說,從抽象概念上來講,新定義類型所代表的事物并不是原有類型所代表事物的一種,比如黃種人是人類的一種,它們之間存在包含與被包含的關系,那么這時組合就是實現復用更好的選擇。下面這個例子就是組合方式的一個簡單示例:
- public ? class ?Sub?{ ??
- ???? private ?Parent?p?=? new ?Parent(); ??
- ??
- ???? public ? void ?doSomething()?{ ??
- ???????? //?復用Parent類的方法 ??
- ????????p.method(); ??
- ???????? //?other?code ??
- ????} ??
- } ??
- ??
- class ?Parent?{ ??
- ???? public ? void ?method()?{ ??
- ???????? //?do?something?here ??
- ????} ??
- }??
public class Sub { private Parent p = new Parent(); public void doSomething() { // 復用Parent類的方法 p.method(); // other code } } class Parent { public void method() { // do something here } }
當然,為了使代碼更加有效,我們也可以在需要使用到原有類型(比如Parent p)時,才對它進行初始化。
使用繼承和組合復用原有的類,都是一種增量式的開發模式,這種方式帶來的好處是不需要修改原有的代碼,因此不會給原有代碼帶來新的BUG,也不用因為對原有代碼的修改而重新進行測試,這對我們的開發顯然是有益的。因此,如果我們是在維護或者改造一個原有的系統或模塊,尤其是對它們的了解不是很透徹的時候,就可以選擇增量開發的模式,這不僅可以大大提高我們的開發效率,也可以規避由于對原有代碼的修改而帶來的風險。
多態(Polymorphism)
多態是又一個重要的基本概念,上面說到了,它是面向對象的三個基本特征之一。究竟什么是多態呢?我們先看看下面的例子,來幫助理解:
- //汽車接口 ??
- interface ?Car?{ ??
- ???? //?汽車名稱 ??
- ????String?getName(); ??
- ??
- ???? //?獲得汽車售價 ??
- ???? int ?getPrice(); ??
- } ??
- ??
- //?寶馬 ??
- class ?BMW? implements ?Car?{ ??
- ???? public ?String?getName()?{ ??
- ???????? return ? "BMW" ; ??
- ????} ??
- ??
- ???? public ? int ?getPrice()?{ ??
- ???????? return ? 300000 ; ??
- ????} ??
- } ??
- ??
- //?奇瑞QQ ??
- class ?CheryQQ? implements ?Car?{ ??
- ???? public ?String?getName()?{ ??
- ???????? return ? "CheryQQ" ; ??
- ????} ??
- ??
- ???? public ? int ?getPrice()?{ ??
- ???????? return ? 20000 ; ??
- ????} ??
- } ??
- ??
- //?汽車出售店 ??
- public ? class ?CarShop?{ ??
- ???? //?售車收入 ??
- ???? private ? int ?money?=? 0 ; ??
- ??
- ???? //?賣出一部車 ??
- ???? public ? void ?sellCar(Car?car)?{ ??
- ????????System.out.println( "車型:" ?+?car.getName()?+? "??單價:" ?+?car.getPrice()); ??
- ???????? //?增加賣出車售價的收入 ??
- ????????money?+=?car.getPrice(); ??
- ????} ??
- ??
- ???? //?售車總收入 ??
- ???? public ? int ?getMoney()?{ ??
- ???????? return ?money; ??
- ????} ??
- ??
- ???? public ? static ? void ?main(String[]?args)?{ ??
- ????????CarShop?aShop?=? new ?CarShop(); ??
- ???????? //?賣出一輛寶馬 ??
- ????????aShop.sellCar( new ?BMW()); ??
- ???????? //?賣出一輛奇瑞QQ ??
- ????????aShop.sellCar( new ?CheryQQ()); ??
- ????????System.out.println( "總收入:" ?+?aShop.getMoney()); ??
- ????} ??
- }??
//汽車接口 interface Car { // 汽車名稱 String getName(); // 獲得汽車售價 int getPrice(); } // 寶馬 class BMW implements Car { public String getName() { return "BMW"; } public int getPrice() { return 300000; } } // 奇瑞QQ class CheryQQ implements Car { public String getName() { return "CheryQQ"; } public int getPrice() { return 20000; } } // 汽車出售店 public class CarShop { // 售車收入 private int money = 0; // 賣出一部車 public void sellCar(Car car) { System.out.println("車型:" + car.getName() + " 單價:" + car.getPrice()); // 增加賣出車售價的收入 money += car.getPrice(); } // 售車總收入 public int getMoney() { return money; } public static void main(String[] args) { CarShop aShop = new CarShop(); // 賣出一輛寶馬 aShop.sellCar(new BMW()); // 賣出一輛奇瑞QQ aShop.sellCar(new CheryQQ()); System.out.println("總收入:" + aShop.getMoney()); } }
運行結果:
- 車型:BMW? 單價:300000
- 車型:CheryQQ? 單價:20000
- 總收入:320000
繼承是多態得以實現的基礎。從字面上理解,多態就是一種類型(都是Car類型)表現出多種狀態(寶馬汽車的名稱是BMW,售價是300000;奇瑞汽車的名稱是CheryQQ,售價是2000)。將一個方法調用同這個方法所屬的主體(也就是對象或類)關聯起來叫做綁定,分前期綁定和后期綁定兩種。下面解釋一下它們的定義:
- 前期綁定:在程序運行之前進行綁定,由編譯器和連接程序實現,又叫做靜態綁定。比如static方法和final方法,注意,這里也包括private方法,因為它是隱式final的。
- 后期綁定:在運行時根據對象的類型進行綁定,由方法調用機制實現,因此又叫做動態綁定,或者運行時綁定。除了前期綁定外的所有方法都屬于后期綁定。
多態就是在后期綁定這種機制上實現的。多態給我們帶來的好處是消除了類之間的耦合關系,使程序更容易擴展。比如在上例中,新增加一種類型汽車的銷售,只需要讓新定義的類繼承Car類并實現它的所有方法,而無需對原有代碼做任何修改,CarShop類的sellCar(Car car)方法就可以處理新的車型了。新增代碼如下:
- //?桑塔納汽車 ??
- class ?Santana? implements ?Car?{ ??
- ???? public ?String?getName()?{ ??
- ???????? return ? "Santana" ; ??
- ????} ??
- ??
- ???? public ? int ?getPrice()?{ ??
- ???????? return ? 80000 ; ??
- ????} ??
- }??
// 桑塔納汽車 class Santana implements Car { public String getName() { return "Santana"; } public int getPrice() { return 80000; } }
重載(overloading)和重寫(overriding)
重載和重寫都是針對方法的概念,在弄清楚這兩個概念之前,我們先來了解一下什么叫方法的型構(英文名是signature,有的譯作“簽名”,雖然它被使用的較為廣泛,但是這個翻譯不準確的)。型構就是指方法的組成結構,具體包括方法的名稱和參數,涵蓋參數的數量、類型以及出現的順序,但是不包括方法的返回值類型,訪問權限修飾符,以及abstract、static、final等修飾符。比如下面兩個就是具有相同型構的方法:
- public ? void ?method( int ?i,?String?s)?{ ??
- ???? //?do?something ??
- } ??
- ??
- public ?String?method( int ?i,?String?s)?{ ??
- ???? //?do?something ??
- }??
public void method(int i, String s) { // do something } public String method(int i, String s) { // do something }
而這兩個就是具有不同型構的方法:
- public ? void ?method( int ?i,?String?s)?{ ??
- ???? //?do?something ??
- } ??
- ??
- public ? void ?method(String?s,? int ?i)?{ ??
- ???? //?do?something ??
- }??
public void method(int i, String s) { // do something } public void method(String s, int i) { // do something }
了解完型構的概念后我們再來看看重載和重寫,請看它們的定義:
- 重寫,英文名是overriding,是指在繼承情況下,子類中定義了與其基類中方法具有相同型構的新方法,就叫做子類把基類的方法重寫了。這是實現多態必須的步驟。
- 重載,英文名是overloading,是指在同一個類中定義了一個以上具有相同名稱,但是型構不同的方法。在同一個類中,是不允許定義多于一個的具有相同型構的方法的。
我們來考慮一個有趣的問題:構造器可以被重載嗎?答案當然是可以的,我們在實際的編程中也經常這么做。實際上構造器也是一個方法,構造器名就是方法名,構造器參數就是方法參數,而它的返回值就是新創建的類的實例。但是構造器卻不可以被子類重寫,因為子類無法定義與基類具有相同型構的構造器。
?
向原作者致敬,轉自:
作者:臧圩人(zangweiren)
網址:
http://zangweiren.iteye.com
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
