1. 程式人生 > >多型機制原理解析--從記憶體角度分析

多型機制原理解析--從記憶體角度分析

回頭看多型,又有了新的認識。
理解多型主要搞清楚以下幾個問題就可以了:(以父類Pet,子類Dog為例)
1.為什麼可以用表示式 Pet p1= new Dog(); //為什麼可以用父類的引用指向子類建立的物件?
2. 當用父類的引用p1,操作子類的物件的時候,為什麼可以操作複寫(override)的Method,怎麼知道該Method是子類的而不是父類的?
3. 當用父類的引用p1操作子類的時候,有哪些限制?

下面進行解答:
理解多型的前提是理解繼承(面向物件的三個基本特徵:封裝、繼承、多型)
繼承在此不做多的解釋,只需要知道兩點:1.父類中private 方法或者屬性是不能夠被繼承到子類中;2. 子類實際又兩部分組成,一部分是從父類那裡copy過來的,還有一部分是自己的。從記憶體的角度來說,子類在初始化的時候,先初始化繼承的那部分,也就是父類的東西(這裡的初始化本質可以理解為複製,後面會通過例項進行詳細解讀),再初始化自己特有的部分。

例如下圖,父類Pet中有屬性a、還有三種不同型別的sayX方法。子類Dog繼承了父類的屬性a、public say()以及Protected say1(),還有自己的方法cute。

這裡寫圖片描述

下面說一下多型機制的原理

  1. 執行Pet p1=new Dog(); p1雖然是Pet型別的引用,但實際仍然指向Dog,只是操作範圍只有從父類繼承來的哪些屬性或者方法。比如,本例中,p1實際操作範圍,只有a,say,say1。不能操作say2,因為say2並沒有繼承到Dog類中。另外,子類在初始化的時候,也會將父類的方法申明所使用的偏移量賦值一份。
  2. JVM在執行class檔案的時候,也就是在記憶體中執行Java的程式的時候,會將父類的Method,放到Method Table
    裡面,子類複寫了父類中的方法後,初始化子類的時候,就會將Method Table中相應的Method進行覆蓋。
    另一方面,所有派生類中繼承於基類的方法在方法表中的偏移量跟該方法在基類方法表中的偏移量保持一致這也就是為什麼可以準確的呼叫Method。
  3. 結合以上兩點:p1.say() 之所以能夠動態繫結實現多型,本質是Method Table進行了維護。之所以可以寫成這樣,Pet p1=new Dog() 是因為繼承,is-a關係:子類是一種父類。

下面看demo:

class Pet {
    int a = 0;

    public void say() {
        System.out.println("Pet say"
); } protected void say2() { System.out.println("Pet say2"); } private void say3() { System.out.println("Pet say3"); } } class Dog extends Pet { public void cute(){ System.out.println("Dog cute"); } public void say(){ System.out.println("Dog say"); } } class Cat extends Pet { public void cute(){ System.out.println("Cat cute"); } public void say(){ System.out.println("Cat say"); } } public class Zoo { public static void main(String[] args) { Pet p1 = new Dog(); p1.say(); //首先解析一次,得到偏移量,呼叫方法 p1.say2();//p1 可以呼叫Protected,不可以呼叫private型別 Pet p0= new Pet(); p0.a=2;//先初始化了子類,後修改父類的屬性,通過列印, //可以知道,繼承後的子類初始化本質是複製,即從父類拷貝了一份“型別+初始值”,然後父類數值上怎麼變,和子類無關。 System.out.println("p0.a=="+p0.a); System.out.println("p1.a=="+p1.a); } Pet p3=new Cat(); p3.say(); }

列印結果:
Dog say
Pet say2
p0.a==2
p1.a==0
Cat say

補充:
1.Java不支援多繼承,通過實現介面的方式來達到多繼承的效果。
2. 實現介面的方式,不同於繼承,所以不能保證相同的偏移量,也就意味著動態繫結會會比較繁瑣,每次都要進行解析。