以前一直知道多態是什麼東西,平時敲代碼也常常用到多態,但一直沒有真正瞭解多態底層的運行機制究竟是怎麼樣的,這兩天才研究明白點,特意寫下來,跟各位同窗一塊兒進步,同時也但願各位大神指導和指正。數組
多態的概念:同一操做做用於不一樣對象,能夠有不一樣的解釋,有不一樣的執行結果,這就是多態,簡單來講就是:父類的引用指向子類對象。下面先看一段代碼數據結構
1 package polymorphism; 2 3 class Dance { 4 public void play(){ 5 System.out.println("Dance.play"); 6 } 7 public void play(int i){ 8 System.out.println("Dance.play" + i); 9 } 10 } 11 12 class Latin extends Dance { 13 public void play(){ 14 System.out.println("Latin.play"); 15 } 16 public void play(char c){ 17 System.out.println("Latin.play" + c); 18 } 19 } 20 class Jazz extends Dance { 21 public void play(){ 22 System.out.println("Jazz.play"); 23 } 24 public void play(double d){ 25 System.out.println("Jazz.play" + d); 26 } 27 } 28 public class Test { 29 public void perform(Dance dance){ 30 dance.play(); 31 } 32 public static void main(String[] args){ 33 new Test().perform(new Latin()); // Upcasting 34 } 35 }
執行結果:Latin.play。這個時候你可能會發現perform()方法裏面並無相似「if 參數類型 = Dance/Latin」這樣的判斷,其實這就是多態的特性,它消除了類型之間的耦合關係,令咱們能夠把一個對象不當作它所屬的特定類型來對待,而是當作其基類的類型來對待。由於上面的Test代碼徹底能夠這麼寫:ide
1 public class Test { 2 public void perform(Latin dance){ 3 dance.play(); 4 } 5 public void perform(Jazz dance){ 6 dance.play(); 7 } 8 public static void main(String[] args){ 9 new Test().perform(new Latin()); // Upcasting 10 } 11 }
可是這樣你會發現,若是增長更多新的相似perform()或者自Dance導出的新類,就會增長大量的工做,而經過比較就會知道第一處代碼會佔優點,這正是多態的優勢所在,它改善了代碼的組織結構和可讀性,同時保證了可擴展性。spa
那麼到底JVM是怎麼指向Latin類中play()的呢?爲了解決這個問題,JAVA使用了後期綁定的概念。當向對象發送消息時,在編譯階段,編譯器只保證被調用方法的存在,並對調用參數和返回類型進行檢查,可是並不知道將被執行的確切代碼,被調用的代碼直到運行時才能肯定。拿上面代碼爲例,JAVA在執行後期綁定時,JVM會從方法區中的Latin方法表中取到Latin對象的直接地址,這就是真正執行結果爲何是Latin.play的緣由,在解釋方法表以前,咱們先了解一下綁定的概念。code
將一個方法調用同一個方法主體關聯起來被稱做綁定,JAVA中分爲前期綁定和後期綁定(動態綁定或運行時綁定),在程序執行以前進行綁定(由編譯器和鏈接程序實現)叫作前期綁定,由於在編譯階段被調用方法的直接地址就已經存儲在方法所屬類的常量池中了,程序執行時直接調用,具體解釋請看最後參考資料地址。後期綁定含義就是在程序運行時根據對象的類型進行綁定,想實現後期綁定,就必須具備某種機制,以便在運行時能判斷對象的類型,從而找到對應的方法,簡言之就是必須在對象中安置某種「類型信」,JAVA中除了static方法、final方法(private方法屬於)以外,其餘的方法都是後期綁定。後期綁定會涉及到JVM管理下的一個重要的數據結構——方法表,方法表以數組的形式記錄當前類及其全部父類的可見方法字節碼在內存中的直接地址。orm
動態綁定具體的調用過程爲:對象
1.首先會找到被調用方法所屬類的全限定名blog
2.在此類的方法表中尋找被調用方法,若是找到,會將方法表中此方法的索引項記錄到常量池中(這個過程叫常量池解析),若是沒有,編譯失敗。索引
3.根據具體實例化的對象找到方法區中此對象的方法表,再找到方法表中的被調用方法,最後經過直接地址找到字節碼所在的內存空間。內存
最後說明,域和靜態方法都是不具備多態性的,任何的域訪問操做都將由編譯器解析,所以不是多態的。靜態方法是跟類,而並不是單個對象相關聯的。對動態綁定還有不明白的請看資料連接,我的感受分析的很到位