多型:多型通過分離做什麼和怎麼做,從另一角度將介面和實現分離出來,有些文章將過載視作編譯時多型(其實個人感覺並沒有多型的特性),將重寫視為執行時多型
先來看一段程式碼:
class Father {
protected String type = "Father";
public Father() {
}
public void getType() {
System.out.println(type);
}
}
class Son extends Father {
protected String type = "Son";
public Son() {
}
@Override
public void getType() {
System.out.println(type);
}
}
public class TestDynamicBind {
public void getType(Father father) {
System.out.println("this is father interface");
father.getType();
}
public void getType(Son son) {
System.out.println("this is son interface");
son.getType();
}
public static void main(String[] args) {
Father son = new Son();
System.out.println(son.type); --1、Father
son.getType(); -- 2、Son
TestDynamicBind bind = new TestDynamicBind();
bind.getType(son); -- 3、this is father interface/Son
bind.getType(new Son()); --4、this is son interface/Son
}
}
複製程式碼
程式的執行結果為:
Father
Son
this is father interface
Son
this is son interface
Son
複製程式碼
- 1列印type屬性,注意
域和靜態方法是無法使用多型的
,因此列印域值為Father - 2通過物件的例項方法列印type屬性,雖然變數son的宣告型別為Father,但實際為Son型別物件,執行例項方法時會根據
呼叫物件的方法表
(如果物件為子類,改寫了父類的方法,那麼子類和父類的那些同名的方法共享一個方法表項,所有繼承父類的子類的方法表中,重寫的父類所定義的方法的偏移量也總是一個定值,這樣 JVM 在呼叫例項方法時只需要指定呼叫方法表中的第幾個方法即可)實際呼叫Son class的getType方法 - 3呼叫TestDynamicBind的getType方法,注意該類有兩個getType方法(也就是包含兩個過載的getType方法),執行時應該調哪一個呢,其實編譯時就決定了(所以個人覺得並不具備多型的特性),因為執行時是根據引數型別決定呼叫方法的,
而引數型別是編譯時期決定的,雖然物件實際為Son型別
,後面會展示相關位元組碼,因此走入了getType(Father father)方法,但實際呼叫物件的getType方法時由於該物件為Son型別而呼叫了Son的getType方法,輸出Son - 4直接構造了new Son()物件傳入TestDynamicBind物件的getType方法,這種方式和Son son = new Son();bind.getType(son);的效果是一致的,因此走入了TestDynamicBind的getType(Son son)方法且輸出Son
上位元組碼
- 上面是main方法的位元組碼,可以看到TestDynamicBind的兩次getType呼叫(後兩個標註)都是編譯期決定了引數型別(LFather/LSon),實際也確定了呼叫哪個方法,雖然位元組碼命令為invokevirtual
- 對於物件例項方法的多型呼叫,繼承父類和實現介面的實現機制雖有不同(繼承的方法在父類和子類的方法表項的位置是相同的,而實現介面的方法在不同實現類的方法表項的位置很可能是不同的),但實際呼叫時都走了各自物件的具體實現方法
歡迎關注微信訂閱號