歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java:方法的虛分派(virtual dispatch)和方法表(method table)

背景知識:

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訪問的不同。

  1. public class Greeting {  
  2.     String intro = "Hello";  
  3.     String target(){  
  4.         return "world";  
  5.     }  
  6. }  
 
  1. public class FrenchGreeting extends Greeting {  
  2.     String intro = "Bonjour";  
  3.     String target(){  
  4.         return "le monde";  
  5.     }  
  6.       
  7.       
  8.     public static void main(String[] args){  
  9.         Greeting english = new Greeting();  
  10.         Greeting french = new FrenchGreeting();  
  11.           
  12.         System.out.println(english.intro + "," + english.target());  
  13.         System.out.println(french.intro + "," + french.target());  
  14.         System.out.println(((FrenchGreeting)french).intro + "," + ((FrenchGreeting)french).target());  
  15.     }  
  16. }  
運行的結果為
  1. Hello,world  
  2. Hello,le monde  
  3. Bonjour,le monde  
其中的第二行是亮點。

 對於intro這個filed的訪問,直接指向了父類中的變量,因為引用 類型為父類。

而對於target的方法調用,則是指向了子類中的方法,雖然引用類型也為父類,但這是virtual dispatch的結果,virtual dispatch不管引用類型的,只查receiver的類型。


既然 虛分派 機制是從receiver對象的子類開始查找,由此看來,對於一個覆蓋了父類中某方法的子類的對象,是無法調用父類中那個被覆蓋的方法的嗎?

在虛分派機制中這確實是不可以的。但卻可以通過invokespecial實現。如下代碼

  1. public class FrenchGreeting extends Greeting {  
  2.     String intro = "Bonjour";  
  3.     String target(){  
  4.         return "le monde";  
  5.     }  
  6.       
  7.     public String func(){  
  8.         return super.target();  
  9.     }  
  10.       
  11.       
  12.     public static void main(String[] args){  
  13.         Greeting english = new Greeting();  
  14.         FrenchGreeting french = new FrenchGreeting();  
  15.           
  16.         System.out.println(french.func());  
  17.           
  18.     }  
  19. }  
func()就成功的調用了父類的方法target,雖然target已經被子類重寫了。怎麼實現的?讓我們看一看func方法中生成的字節碼:
  1. ALOAD 0: this  
  2. INVOKESPECIAL Greeting.target() : String  
  3. ARETURN  
原來如此,它是通過invokespecial 指令來調用的。
Copyright © Linux教程網 All Rights Reserved