OOP語言中,多態是封裝、繼承以後的第三種基本特徵。程序員
封裝:經過合併特徵和行爲來建立新的數據類型,「實現隱藏」經過細節「私有化」把接口和實現分離。安全
繼承:以複用接口方式從已有類型用extends關鍵字建立新類型,並容許向上轉型。spa
多態:消除類型之間的耦合關係(分離作什麼和怎麼作),基於繼承的向上轉型功能,容許同一種類型同一行爲有不一樣的表現。設計
8.1再論向上轉型對象
8.1.1忘記對象類型繼承
無論導出類的存在,編寫的代碼(方法)只是針對基類類型。不須要爲每一個導出類型都寫各自的代碼,這正是多態所容許的。接口
8.2起色內存
程序運行時接受的是基類類型,可是它如何知道具體類型是哪個從而調用正確的方法呢?咱們須要瞭解綁定機制。編譯器
8.2.1方法調用綁定io
把方法調用同方法主體關聯起來稱爲綁定。
前期綁定:程序執行前綁定(由編譯器和鏈接程序實現),C語言中方法調用都是前期綁定。
後期綁定:又叫動態綁定,運行時綁定,在運行時根據對象的類型綁定對應的方法主體。
Java中默認就是動態綁定,無需手動設置。特殊:static方法和final(private也是final)方法不存在多態性,不是動態綁定。
8.2.2產生正確行爲
動態綁定使得多態中的基類對象能夠正確執行相應的導出類對象方法。
8.2.3可擴展性
多態使得擴展新類型和擴展基類不會對已有代碼(調用基類方法的代碼)產生影響。它可讓程序員「將改變的事物與不變的事物分離開」。
8.2.4缺陷:不能夠覆蓋private方法
基類中private方法在子類中能夠用相同的方法名和簽名,可是它是一個全新的方法,不會按照咱們想要的子類方法來執行。
調用的時候,按照基類方法的訪問權限來決定是否能夠調用。
子類是否會覆蓋父類方法,按照子類是否能夠訪問到父類該方法來決定是否能夠覆蓋。
8.2.5缺陷:域與靜態方法
多態特性(動態綁定)只是針對方法的。域和靜態方法不具備這種特性。
如:父類和子類都有一個域 public String str; 在Super s = new Sub(); s.str 取出的是Super裏的而不是Sub裏的。 不過通常狀況不會存在這種把域設置爲public而且想用子類覆蓋它的狀況。
靜態方法也不會有多態性。
8.3構造器和多態
構造器是隱式static方法,不具備多態特性。
8.3.1構造器的調用順序
爲何編譯器強制每一個導出類的構造器必須調用基類構造器呢:由於構造器有個特殊的任務,檢查對象是否被正確構造。導出類構造器只能訪問它本身的成員,不能訪問基類的成員(一般是private成員)。只有基類構造器才具備相應的知識和權限對本身的元素進行初始化。而導出類成員的初始化有可能會用到基類成員,所以導出類初始化在基類以後。
8.3.2繼承與清理
經過組合和繼承方式建立新類時,一般狀況都是不須要擔憂對象的清理問題。
可是若是的確須要作清理時,必須很是當心謹慎:在使用完以後按照建立逆序清理,即sub.dispose()而後super.dispose()來清理。
更加複雜的狀況:不知道何時使用結束,須要本身定義引用計數,而後再清理。
8.3.3構造器內部的多態方法行爲
在調用子類構造器的過程當中,會先調用父類構造器,此時子類構造器還沒調用完成,子類對象也沒有執行初始化,若是在父類構造器裏調用多態方法,那麼這個方法是能夠產生多態行爲特徵的,可是因爲子類構造器沒有執行完,所以子類的初始化還沒完成,多態方法裏對子類成員變量的獲取只能拿到默認值0,false,null
對象初始化過程(注意與類的加載過程區分):
1.給導出類對象分配內存空間,並初始化爲0,false,null
2.調用父類構造器,並執行多態方法,拿到的是子類0,false,null的域
3.按照聲明順序調用成員變量的初始化
4.調用子類構造器主體
此處雖然邏輯沒什麼問題,可是行爲的確錯誤了,因此在寫構造器的時候,咱們要儘量用簡單的方法使對象進入正常狀態,若是能夠的話避免調用其餘方法。
8.4協變返回類型
導出類重寫父類方法,方法的返回類型(區分返回值)能夠是父類返回類型的某一個導出類。
8.5用繼承進行設計
就建立新類型而言,不要只想到繼承,應該優先考慮組合,它比繼承具備更大的靈活性,能夠動態的改變類型,而繼承在編譯時類型已經肯定了。
8.5.1純繼承與擴展
純粹的繼承:基類接口與導出類徹底一致,是is-a關係
擴展:導出類除了基類接口外,還有其餘方法,是is-like-a關係,可是這些擴展方法不能以基類引用去調用
8.5.2向下轉型與運行時類型識別
向上轉型是安全的:基類不會有大於導出類的接口
向下轉型須要確保類型的正確性:Java中類型轉換(括號強轉)都會進行類型檢查,不正確拋出ClassCastException。這種在運行期間對類型進行檢查的行爲稱做「運行時類型識別」(RTTI)。
8.6總結
多態意味着「不一樣的形式」。在OOP裏,咱們持有基類的相同接口,使用該接口的不一樣形式:不一樣版本的動態綁定。