Java 虛擬調用(virtual invoke)分析

此文章來分析下,Java 的虛擬調用。
When we say Java language has virtual method calling we mean that in java applications the executed method is determined by the object type in run time.
Java的虛擬方法調用,指的是,調用的方法是由對象運行時的類型決定的。
那麼什麼是虛擬方法呢?

虛擬方法

我們看下維基百科的介紹,
In object-oriented programming, a virtual function or virtual method is a function or method whose behavior can be overridden within an inheriting class by a function with the same signature. This concept is an important part of the polymorphism portion of object-oriented programming (OOP).
簡單翻譯一下,
在面向對象編程時,一個虛擬函數或方法,指的是一個可以被繼承類重寫的函數或方法。該概念是面向對象編程多態的一個重要方面。
OK,簡單來說,虛擬方法,就是可以被子類重寫的方法。
讓我們先看一個例子,
首先我們定義一個基類,

public class BaseClass {
    public void methodA() {

    };
}

ClassA和ClassB分別是BaseClass的子類,

public class ClassA extends BaseClass{
    public void methodA() {
        System.out.println("call methodA in ClassA! ");
    }
}
public class ClassB extends BaseClass {
    public void methodA() {
        System.out.println("call methodA in ClassB!");
    }
}

然後,我們的測試代碼,

    public static void main(String[] args) {
        BaseClass a = new ClassA();
        a.methodA();

        BaseClass b = new ClassB();
        b.methodA();
    }

在此例子中,methodA被子類繼承,則是虛擬方法,讓我們運行一下main方法,看下輸出結果是啥,

call methodA in ClassA! 
call methodA in ClassB!

此時,我們再看上面提到的虛擬方法調用的定義,調用的方法是由運行時的對象類型決定的。我們來看這個例子,當我們實例化一個ClassA,並賦給a時,此時a的類型表面上是BaseClass,但是其類型卻是ClassA,所以執行a.methodA()時,會調用ClassA的方法。此時,你或許會疑問,a的聲明類型是BaseClass,爲什麼運行時的類型是ClassA呢?這也就是Java多態的表現之一。
注意,上面的基類BaseClass,也可以用接口來實現,結果也是一樣的。
讓我們再看一個複雜一點的例子,
我們修改BaseClass代碼如下,

public class BaseClass {
    public void methodA() {
        System.out.println("call methodA in BaseClass!");
        methodB();
    };

    public void methodB() {
        System.out.println("call methodB in BaseClass!");
    };
}

定義了兩個方法,並且methodA中調用methodB,
ClassA繼承BaseClass,並重寫methodA和methodB方法

public class ClassA extends BaseClass{
    public void methodA() {
        super.methodA();
        System.out.println("call methodA in ClassA! ");
    }
    public void methodB() {
        System.out.println("call methodB in ClassA! ");
    }
}

我們測試代碼如下,

public class Main {
    public static void main(String[] args) {
        BaseClass a = new ClassA();
        a.methodA();
    }
}

我們來預測一下,輸出結果會是什麼。
首先根據上個例子,我們可以知道a.methodA()調用的會是ClassA的methodA方法,然後Class.methodA調用父類的methodA方法,父類的methodA又調用methodB方法,那麼預期結果應該是這樣的,

call methodA in BaseClass!
call methodB in BaseClass! 
call methodA in ClassA! 

然而,運行結果卻是這樣的,

call methodA in BaseClass!
call methodB in ClassA! 
call methodA in ClassA! 

運行結果與預期結果唯一的不同,在於父類的methodA方法調用methodB方法,是調用自己的?還是調用子類的呢?根據運行結果,我們知道,父類(BaseClass)的methodA方法此時調用的是子類(ClassA)的methodB方法。
讓我們再來分析下,當運行a.method()時,會調用super.methodA(),注意此時調用對象是a,也就是ClassA,super.methodA又調用了methodB,根據虛擬調用,由於調用對象類型是ClassA,於是調用ClassA的methodB方法。

根據上面兩個例子,希望讀者能理解虛擬調用的思想。OPP是個很深奧的東西,希望大家多多學習。

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