參考博客:
【解惑】Java動態綁定機制的內幕:http://blog.csdn.net/zero_295813128/article/details/52117737
Java 虛擬機體系結構:http://hxraid.iteye.com/blog/676235
--------------------------------------------------------------------------------------------------
下面是自己的理解總結:
首先得對兩個概念有理解方法區和常量池。這兩個都是Java虛擬在運行時的數據區域,常量池是方法區
的一部分。
方法區
方法區與Java堆一樣,是各個線程共享的內存區域,用於存儲已經被虛擬機加載的類信息、常量、靜態變
量、即使編譯器編譯後的代碼等數據。
常量池
常量池是方法區的一部分,用於存放編譯期所生成的各種字面量和符號引用。常量池中共有11個常量表。
常量表類型 標誌值(佔1 byte) 描述
CONSTANT_Utf8 1 UTF-8編碼的Unicode字符串
CONSTANT_Integer 3 int類型的字面值
CONSTANT_Float 4 float類型的字面值
CONSTANT_Long 5 long類型的字面值
CONSTANT_Double 6 double類型的字面值
CONSTANT_Class 7 對一個類或接口的符號引用
CONSTANT_String 8 String類型字面值的引用
CONSTANT_Fieldref 9 對一個字段的符號引用
CONSTANT_Methodref 10 對一個類中方法的符號引用
CONSTANT_InterfaceMethodref 11 對一個接口中方法的符號引用
CONSTANT_NameAndType 12 對一個字段或方法的部分符號引用
---------------------------------------------------------------------------------------------------
對於多態的原理主要研究方法區。在Java中多態指的是它允許基類的指針或引用指向派生類的對象,而在具體訪
問時實現方法的動態綁定。java中的方法調用有靜態綁定和動態綁定之分,靜態綁定指的是我們在編譯期就已經
確定了會執行那個方法的字節碼,而動態綁定只有在運行時才能知曉。
靜態綁定
Java中的靜態方法、私有方法以及final修飾的方法的調用,都屬於靜態綁定,對於重載的實例方法的
調用,也是採用靜態綁定。靜態綁定的原理主要是一個常量池解析的過程,下面來詳解其過程:
假如有兩個類A、B,在A類中我們調用了B類中的一個靜態方法,在編譯期,這個調用的動作會被編譯成一
條靜態調用指令,該指令就對應常量池中的CONSTANT_Methodref表中所存儲的該方法的符號引用,通過這個符號引用
可以得到靜態方法的全類名B,JVM加載B類,便會得到B類中方法的直接地址,該地址會被存儲到A類常量池中對應的
常量表中,這便是常量池解析過程,再次發起調用時,就會直接根據這個直接地址調用對應方法。
以上過程可以看出,在編譯階段我們就已經確定了應該執行哪一個字節碼代碼。
動態綁定
動態綁定講解之前需要了解JVM管理的一個重要的數據結構--方法表。它以數組的形式記錄了當前類及其所有
超類的可見方法字節碼在內存中的直接地址 。
動態綁定前面的流程與靜態綁定類似,假如此處有兩個類A,B繼承了A類,B類中重寫了A類中的f1()方法,我們採用
向上轉型的方式用指向B實例的A類型引用調用f1()方法,編譯器會生成一條字節碼指令,該指令會去常量表中找到f1()方法信
息的符號引用,通過該引用確定調用該方法的類型全名,即A類的全名稱,根據名稱加載到A類的字節碼,去A類所對應的方法
表中找到f1()方法,將它的直接地址記錄到調用f1()方法的類的對應的常量表中,常量池解析結束,可以思考,我們此時是否能
確定調用f1()方法時執行的是哪一塊的字節碼,答案是不能,因爲截至此時我們f1()方法指定執行的是父類中的方法,引用雖然
父類類型,但他指向的是父類對象還是子類對象是不知道的(確切地說是此時的程序不知道,程序員肯定是知道的),假如指向父類
那就是父類中的f1()方法,如果指向子類的實例,子類沒有重寫,依然執行父類f1()方法,如果子類重寫了就應該是子類的f1()
方法。此時動態綁定就登場了,確定f1()換需要拿到B類實例在堆中的引用,通過引用找到堆中B的對象,根據對象進一步獲取它的
方法表,找到方法表的f1()方法的直接地址,此時便最終確定了。