背景知識:
java 字節碼基本框架,jvm基本框架
多態的機制。
Virtual Dispatch
首先從字節碼中對方法的調用說起。
java的bytecode中對方法的調用實現分為四種情況:
1.invokevirtual 為最常見的情況,包含virtual dispatch機制;
2.invokespecial是作為private和構造方法的調用,繞過了virtual dispatch;
3.invokeinterface的實現跟invokevirtual類似。
4.invokestatic是對靜態方法的調用。
其中最復雜的要屬 invokevirtual .
virtual dispatch機制會首先從 receiver(調用該方法的對象)的類的實現中查找對應的方法,如果沒找到,則去父類查找,直到找到函數並實現調用,而不是依賴於引用類型。
下面是一段有趣的代碼。反映了virtual dispatch機制 和 一般的field訪問的不同。
- public class Greeting {
- String intro = "Hello";
- String target(){
- return "world";
- }
- }
- public class FrenchGreeting extends Greeting {
- String intro = "Bonjour";
- String target(){
- return "le monde";
- }
-
-
- public static void main(String[] args){
- Greeting english = new Greeting();
- Greeting french = new FrenchGreeting();
-
- System.out.println(english.intro + "," + english.target());
- System.out.println(french.intro + "," + french.target());
- System.out.println(((FrenchGreeting)french).intro + "," + ((FrenchGreeting)french).target());
- }
- }
運行的結果為
- Hello,world
- Hello,le monde
- Bonjour,le monde
其中的第二行是亮點。
對於intro這個filed的訪問,直接指向了父類中的變量,因為引用 類型為父類。
而對於target的方法調用,則是指向了子類中的方法,雖然引用類型也為父類,但這是virtual dispatch的結果,virtual dispatch不管引用類型的,只查receiver的類型。
既然 虛分派 機制是從receiver對象的子類開始查找,由此看來,對於一個覆蓋了父類中某方法的子類的對象,是無法調用父類中那個被覆蓋的方法的嗎?
在虛分派機制中這確實是不可以的。但卻可以通過invokespecial實現。如下代碼
- public class FrenchGreeting extends Greeting {
- String intro = "Bonjour";
- String target(){
- return "le monde";
- }
-
- public String func(){
- return super.target();
- }
-
-
- public static void main(String[] args){
- Greeting english = new Greeting();
- FrenchGreeting french = new FrenchGreeting();
-
- System.out.println(french.func());
-
- }
- }
func()就成功的調用了父類的方法target,雖然target已經被子類重寫了。怎麼實現的?讓我們看一看func方法中生成的字節碼:
- ALOAD 0: this
- INVOKESPECIAL Greeting.target() : String
- ARETURN
原來如此,它是通過invokespecial 指令來調用的。