轉載請註明出處:
,前者與類型直接關聯,後者在外部不可被訪問,這兩種方法都不可能通過繼承或別的方式重寫出其他的版本,
Java虛擬機器裡共提供了四條方法調用位元組指令,分別是:
這裡要特別說明下final方法,雖然調用final方法使用的是invokevirtual指令,但是由於它無法覆蓋,沒有其他版本,所以也無需對方發接收者進行多態選擇。)又可分為單指派和多指派。兩類指派方式兩兩組合便構成了靜態單指派、靜態多指派、動態單指派、動態多指派四種指派情況。
靜態指派發生在編譯階段,因此確定靜態分配的動作實際上不是由虛擬機器來執行的。下面通過一段方法重載的樣本程式來更清晰地說明這種指派機制:
class Human{} class Man extends Human{}class Woman extends Human{}public class StaticPai{public void say(Human hum){System.out.println("I am human");}public void say(Man hum){System.out.println("I am man");}public void say(Woman hum){System.out.println("I am woman");}public static void main(String[] args){Human man = new Man();Human woman = new Woman();StaticPai sp = new StaticPai();sp.say(man);sp.say(woman);}}
上面代碼的執行結果如下:
I am human
以上結果的得出應該不難分析。在分析為什麼會選擇參數類型為Human的重載方法去執行之前,先看如下代碼:
,後面的。靜態類型和實際類型在程式中都可以發生一些變化,區別是靜態類型的變化僅僅在使用時發生,變數本身的靜態類型不會被改變,並且最終的靜態類型是在編譯期可知的,而實際類型變化的結果在運行期才可確定。編譯器(不是虛擬機器,因為如果是根據靜態類型做出的判斷,那麼在編譯期就確定了)在重載時是通過參數的靜態類型而不是實際類型作為判定依據的。並且靜態類型是編譯期可知的,所以在編譯階段,Javac編譯器就根據參數的靜態類型決定使用哪個重載版本。這就是靜態指派最典型的應用。
動態指派與多態性的另一個重要體現——方法覆寫有著很緊密的關係。向上轉型後調用子類覆寫的方法便是一個很好地說明動態指派的例子。這種情況很常見,因此這裡不再用樣本程式進行分析。很顯然,在判斷執行父類中的方法還是子類中覆蓋的方法時,如果用靜態類型來判斷,那麼無論怎麼進行向上轉型,都只會調用父類中的方法,但實際情況是,根據對父類執行個體化的子類的不同,調用的是不同子類中覆寫的方法,
class Eat{}class Drink{}class Father{public void doSomething(Eat arg){System.out.println("爸爸在吃飯");}public void doSomething(Drink arg){System.out.println("爸爸在喝水");}}class Child extends Father{public void doSomething(Eat arg){System.out.println("兒子在吃飯");}public void doSomething(Drink arg){System.out.println("兒子在喝水");}}public class SingleDoublePai{public static void main(String[] args){Father father = new Father();Father child = new Child();father.doSomething(new Eat());child.doSomething(new Drink());}}
兒子在喝水