分派發生在編譯期和運行期,編譯期的分派爲靜態分派,運行期的爲動態分派。
編譯期是根據對象聲明的類型來選擇方法,運行期是根據對象實際類型來選擇方法。
- 術語: 宗量(JVM虛擬機) , 什麼是宗量, 方法調用者和方法參數被稱爲宗量.(後面理解分派需要)
- 靜態類型: 一個對象在聲明時的類型稱爲靜態類型,靜態類型再編譯器編譯時可知. 如 Animal a = new Dog(), 靜態類型爲Animal, 實際類型爲Dog.
Java 靜態分派(方法重載)
public class Test{
//hi 方法重載
public void hi(Father f , Father f1){
System.out.println("ff");
}
public void hi(Father f , Son s){
System.out.println("fs");
}
public void hi(Son s , Son s2){
System.out.println("ss");
}
public void hi(Son s , Father f){
System.out.println("sf");
}
public static void main(String[] rags){
Father f = new Father();
Father s = new Son();
Test t = new Test();
t.hi(f , new Father());
t.hi(f , s);
t.dost(s, f);
}
}
class Father {}
class Son extends Father{}
執行結果沒有像預期的那樣輸出 ff、fs、sf而是輸出了三個 ff.
此處對於對象聲明時,靜態類型爲Father, 所以在編譯期間,編譯器會根據參數的靜態類型選擇要執行的方法,此時已經確定要執行的方法,所以在運行時調用的方法爲ff輸出的方法.這就是靜態分派.
Java 動態分派(方法重寫)
public class Test{
public static void main(String[] rags){
Father f = new Father();
Father s = new Son();
System.out.println("f.i " +f.i);
System.out.println("s.i " +s.i);
f.hi();
s.hi();
}
}
class Father {
int i = 0 ;
public void hi(){
System.out.println("WelcomeFather!");
}
}
class Son extends Father{
int i = 9 ;
public void hi(){
System.out.println("WelcomeSon!");
}
}
運行結果:f.i 0 s.i 0 WeclomeFather! WeclomeSon!
變量f,s在編譯器靜態類型爲Father,所以i來自於father, 在運行期間,JVM會根據實際類型來調用方法,s的實際類型爲Son,所以調用的方法是Son重寫的hi方法. 根據實際類型的方法調用爲動態分派.
單分派&多分派
單分派和多分派取決於宗量, 方法調用者和方法參數都是宗量.
Java中靜態分派的方法調用,首先確定調用者的靜態類型是什麼,然後根據要調用的方法參數的靜態類型(聲明類型)確定所有重載方法中要調用哪一個, 需要根據這兩個宗量來編譯, 所以是靜態多分派(多個宗量確定).
Java中動態分派的方法調用,在運行期間,虛擬機會根據調用者的實際類型調用對應的方法, 秩序根據這一個宗量就可以確定要調用的方法,所以是動態單分派(一個宗量)