總結
編譯期間 確定
- 類的靜態方法,構造方法,私有方法
- 方法的重載
運行期間 確定
- 方法的重寫,重載
解析
編譯期間就確定了。
類的靜態方法,構造方法,私有方法。
調用目標在程序代碼寫好、編譯器進行編譯時就必須確定下來。這類方法的調用稱為解析。
在Java語言中符合“編譯期可知,運行期不可變”這個要求的方法,主要包括靜態方法和私有方法兩大類,前者與類型直接關聯,后者在外部不可被訪問,這兩種方法各自的特點決定了它們都不可能通過繼承或別的方式重寫其他版本,因此它們都適合在類加載階段進行解析。
靜態調用(方法重載)
編譯期間就確定了。
多見於方法的重載。
“Human”稱為變量的靜態類型(Static Type),或者叫做的外觀類型(Apparent Type),后面的“Man”則稱為變量的實際類型(Actual Type),靜態類型和實際類型在程序中都可以發生一些變化,區別是靜態類型的變化僅僅在使用時發生,變量本身的靜態類型不會被改變,並且最終的靜態類型是在編譯期可知的;而實際類型變化的結果在運行期才可確定,編譯器在編譯程序的時候並不知道一個對象的實際類型是什么。
代碼中定義了兩個靜態類型相同但實際類型不同的變量,但虛擬機(准確地說是編譯器)在重載時是通過參數的靜態類型而不是實際類型作為判定依據的。並且靜態類型是編譯期可知的,因此,在編譯階段,Javac編譯器會根據參數的靜態類型決定使用哪個重載版本,所以選擇了sayHello(Human)作為調用目標。所有依賴靜態類型來定位方法執行版本的分派動作稱為靜態分派。靜態分派的典型應用是方法重載。靜態分派發生在編譯階段,因此確定靜態分派的動作實際上不是由虛擬機來執行的。
動態調用(方法的重寫,多態)
運行期間才確定。
靜態類型同樣都是Human的兩個變量man和woman在調用sayHello()方法時執行了不同的行為,並且變量man在兩次調用中執行了不同的方法。導致這個現象的原因很明顯,是這兩個變量的實際類型不同。
在實現上,最常用的手段就是為類在方法區中建立一個虛方法表。虛方法表不在方法區,也不在堆也不在棧里。虛擬機維護的,一個類只有一個方法表。虛方法表維護着各個方法的實際入口(方法區中的某個地址)。虛方法表中存放着各個方法的實際入口地址。如果某個方法在子類中沒有被重寫,那子類的虛方法表里面的地址入口和父類相同方法的地址入口是一致的,都指向父類的實現入口。如果子類中重寫了這個方法,子類方法表中的地址將會替換為指向子類實現版本的入口地址。PPT圖中,Son重寫了來自Father的全部方法,因此Son的方法表沒有指向Father類型數據的箭頭。但是Son和Father都沒有重寫來自Object的方法,所以它們的方法表中所有從Object繼承來的方法都指向了Object的數據類型。