理解 Java 方法調用

總結自:《Java 核心技術第 10 版(套裝共 2 冊)-凱 S.霍斯特曼 霍斯特曼 科內爾》

下面假設要調用 x.f(args),隱式參數 x[1] 聲明爲類 C 的一個引用。下面是調用過程的詳細描述:

1)編譯器查看對象的聲明類型和方法名。假設調用 x.f(param),且隱式參數 x 聲明爲 C 類的對象。需要注意的是:有可能存在多個名字爲 f,但參數類型不一樣的方法。例如,可能存在方法 f(int) 和方法 f(String)。編譯器將會一一列舉所有 C 類[2] 中名爲 f 的方法和其超類中名爲 f 的可訪問到的方法[3](超類的私有方法不可訪問)。

至此,編譯器已獲得所有可能被調用的候選方法。

2)接下來,編譯器將查看調用方法時提供的參數類型。如果在所有名爲 f 的方法中存在一個與提供的參數類型完全匹配,就選擇這個方法。這個過程被稱爲重載解析(overloading resolution)。例如,對於調用 x.f("Hello") 來說,編譯器將會挑選 f(String),而不是 f(int)。由於允許類型轉換(int 可以轉換成 double,Manager 可以轉換成 Employee,等等),所以這個過程可能很複雜。如果編譯器沒有找到與參數類型匹配的方法,或者發現經過類型轉換後有多個方法與之匹配,就會報告一個錯誤。[4]

至此,編譯器已獲得需要調用的方法名字和參數類型。

3)如果是 private 方法、static 方法、final 方法或者構造器,那麼編譯器將可以準確地知道應該調用哪個方法[5],我們將這種調用方式稱爲靜態綁定(static binding)。與此對應的是,調用的方法依賴於隱式參數的實際類型,並且在運行時實現動態綁定

4)當程序運行,並且採用動態綁定調用方法時,虛擬機一定調用與 x 所引用對象的實際類型最合適的那個類的方法。假設 x 的實際類型是 D,它是 C 類的子類。如果 D 類定義了方法 f(String),就直接調用它;否則,將在 D 類的超類中尋找 f(String),以此類推。

每次調用方法都要進行搜索,時間開銷相當大。因此,虛擬機預先爲每個類創建了一個方法表(method table)[6],其中列出了所有方法的簽名和實際調用的方法。這樣一來,在真正調用方法的時候,虛擬機僅查找這個表就行了。在前面的例子中,虛擬機搜索 D 類的方法表,以便尋找與調用 f(Sting) 相匹配的方法。這個方法既有可能是 D.f(String),也有可能是 X.f(String),這裏的 X 是 D 的超類。注意,如果調用 super.f(param),編譯器將對隱式參數超類的方法表進行搜索。

比如 Employee 和 Manager 類:

虛擬機爲 Employee 和 Manager 兩個類生成方法表:

將 e 聲明爲 Employee 類型。在運行時,由於 getSalary 不是 private 方法、static 方法或 final 方法,所以將採用動態綁定。調用 e.getSalary()的解析過程爲:

1)首先,虛擬機提取 e 的實際類型的方法表。既可能是 Employee、Manager 的方法表,也可能是 Employee 類的其他子類的方法表。

2)接下來,虛擬機搜索定義 getSalary 簽名的類。此時,虛擬機已經知道應該調用哪個方法。

3)最後,虛擬機調用方法。


  1. 在面向對象編程中,x 通常不視爲參數,但如果把 f 看作函數,那麼 x 及 args 都是這個函數的參數,作爲區分,把 x 稱爲隱式參數,args 稱爲顯式參數。 ↩︎

  2. 注意是列舉 C 類中的 f 方法而不是列出實際對象中的所有 f 方法,因爲對於類型爲 C 類的引用,無法調用 C 類子類中新建的方法。 ↩︎

  3. The compiler enumerates all methods called f in the class C and all accessible methods called f in the superclasses of C. ↩︎

  4. 另見 Java 方法重載中的參數匹配原則 ↩︎

  5. 因爲這些方法都不會被覆蓋。注意,如果在父類和子類中聲明瞭相同方法簽名的靜態方法,將會按隱式參數的聲明類型調用對應的靜態方法(如果使用父類的類型來調用靜態方法,那麼將調用父類中的靜態方法,另一方面如果使用子類的類型來調用靜態方法,則會調用來自子類的方法)。[7] ↩︎

  6. 編譯後保存在 class 文件中,加載後相關信息進入方法區,以便虛擬機在運行時執行方法 ↩︎

  7. Java 類中可以覆蓋靜態方法嗎? ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章