虛擬機字節碼執行引擎隨記(二)方法重寫與動態分派的關係

先看下面這一段代碼:

public class DynamicDispatching {

    abstract class Animal{
        abstract void run();
    }

    class Cat extends Animal{

        @Override
        void run() {
            System.out.println("Cat run.");
        }
    }

    class Dog extends Animal{

        @Override
        void run() {
            System.out.println("Dog run.");
        }
    }

    public static void main(String[] args) {
        DynamicDispatching dynamicDispatching = new DynamicDispatching();
        Animal cat = dynamicDispatching.new Cat();
        cat.run();
        Animal dog = dynamicDispatching.new Dog();
        dog.run();
    }
}

相信這段程序的輸出結果不會出乎任何熟悉面向對象的程序員的意料:

產生這個結果的原因是虛擬機是根據變量的實際類型來判斷調用哪一個重寫的方法。也就是說,

在重寫時,是通過變量的實際類型來判斷使用哪一個方法。

前面介紹了動態分派的過程,而虛擬機是具體是怎麼做到動態分派的呢?

由於動態分派是一個非常頻繁的操作,而動態分派的方法版本選擇需要運行時在類的方法元數據中搜索合適的目標方法,因此虛擬機的大部分實現中出於性能考慮都不會真正地進行如此頻繁的搜索。

爲了解決這個問題,最穩定的解決方法就是在方法區中爲類建一個虛方法表:

虛方法表中存儲了各個方法的實際入口地址,(子類的虛方法表中)子類沒有重寫的方法指向和父類一致的地址,被子類重寫了的方法(子類的虛方法表中)會被替換爲指向子類版本方法的入口地址。

虛方法表一般在類加載的連接階段進行初始化,虛擬機在準備了類變量的初始值後就會把類的虛方法表也初始化完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章