多態(tài)是一個運行時的行為毒姨,不是編譯時行為衣陶。
程序綁定的概念:
綁定指的是一個方法的調用與方法所在的類或對象(方法主體)關聯(lián)起來财饥。對java來說墩衙,綁定分為靜態(tài)綁定和動態(tài)綁定务嫡;或者叫做前期綁定和后期綁定。
靜態(tài)綁定(前期綁定):
在程序執(zhí)行前方法已經被綁定漆改,此時由編譯器或其它連接程序實現(xiàn)心铃。發(fā)生在編譯階段,綁定的是類信息挫剑,即為定義的類的類型去扣。
針對java簡單的可以理解為程序編譯期的綁定;這里特別說明一點樊破,java當中的方法愉棱,只有final,static哲戚,private奔滑,重載方法(overloaded methods)和構造方法是靜態(tài)綁定。
所有的變量都是靜態(tài)綁定顺少。
動態(tài)綁定(后期綁定):
在運行時根據具體對象的類型進行綁定朋其。發(fā)生在運行階段王浴,綁定的是對象信息。
若一種語言實現(xiàn)了后期綁定令宿,同時必須提供一些機制,可在運行期間判斷對象的類型腕窥,并分別調用適當的方法粒没。也就是說,編譯器此時依然不知道對象的類型簇爆,但方法調用機制能自己去調查癞松,找到正確的方法主體。不同的語言對后期綁定的實現(xiàn)方法是有所區(qū)別的入蛆。但我們至少可以這樣認為:它們都要在對象中安插某些特殊類型的信息响蓉。
重寫方法(overridden methods)使用的是動態(tài)綁定
動態(tài)綁定的過程:
- 虛擬機提取對象實際類型的方法表;
- 虛擬機搜索方法簽名哨毁;
- 調用方法枫甲。
- 編譯器檢查對象的聲明類型和方法名。
假設我們調用p.method()方法扼褪,并且p已經被聲明為Child類型想幻,那么編譯器會列舉出Child類中所有的名稱為method的方法和從Child類的父類繼承過來的method方法,如果發(fā)現(xiàn)子類重寫了父類的方法话浇,則父類方法被覆蓋脏毯。 - 接下來編譯器檢查方法調用中提供的參數類型。如果在所有名稱為method的方法中有一個參數類型和調用提供的參數類型最為匹配幔崖,那么就調用這個方法食店,這個過程叫做“重載解析”
重載解析會匹配最精確的一個,一個方法或構造器可以接受傳遞給另一個方法或構造器的任何參數赏寇,那么我們就說第一個比第二個方法缺乏精確性[JLS 15.12.2.5]吉嫩。 - 當程序運行并且使用動態(tài)綁定調用方法時,虛擬機必須調用同p所指向的對象的實際類型相匹配的方法版本嗅定。假設child類定義了mehod()那么該方法被調用率挣,否則就在child的父類(Parent類)中。
- 動態(tài)綁定時露戒,參數會自動進行向上類型椒功,但不會進行向下類型轉換。
補充
這里容易混淆的點是靜態(tài)綁定和動態(tài)綁定的區(qū)分智什,以及到底我們寫的代碼會調用什么方法动漾,這里寫了一個 Test 以供分析:
DynamicBingingTest.java
特別說明的一點是,private聲明的方法和成員變量不能被子類繼承荠锭,所有的private方法都被隱式的指定為final的旱眯,所以是靜態(tài)綁定的。(由此我們也可以知道:將方法聲明為final類型的一是為了防止方法被覆蓋,二是為了有效的關閉java中的動態(tài)綁定)删豺。
Java泛型會被抹除共虑,但是編譯階段已經確保了泛型類型正確(但是 newInstance()方式可以Bug般地繞過泛型檢查)。