Java虛擬機-方法調用

1.什麼是方法調用

方法調用並不等同於方法的執行,方法調用階段的唯一任務就是確定被調用方法的版本。

2.解析調用

      在編譯期間就能夠確定調用方法的版本稱爲解析調用。什麼樣的方法能夠在編譯期間就確定版本呢?靜態方法 ,構造方法, final修飾的方法都能夠在編譯期間就確定其版本。

舉個例子:

public class AnalyseInvokeDemo {

    public static void method() {
        System.out.println("method");
    }

    public static void main(String[] args) {
        method();
    }
}

使用javap -verbose AnalyseInvokeDemo.class查看字節碼,如圖:

由invokestatic指令得知,在編譯期間就確定調用method方法。

2.靜態分派調用

       父類引用指向子類的實例,父類的類型稱爲變量的靜態類型,子類的類型稱爲變量的實際類型,根據變量的靜態類型確定調用方法版本的調用方式稱爲靜態分派,靜態分派用在方法重載,會選擇一個最爲匹配的方法執行。

實例:

public class StaticAssignInvokeDemo2 {

    static class Parent {

    }

    static class ChildOne extends Parent {

    }

    static class ChildTwo extends Parent {

    }

    public void sayHello(ChildOne childOne) {
        System.out.println("childOne is call");
    }

    public void sayHello(ChildTwo childTwo) {
        System.out.println("childTwo is call");
    }

    public void sayHello(Parent parent) {
        System.out.println("parent is call");
    }

    public static void main(String[] args) {
        StaticAssignInvokeDemo2 demo = new StaticAssignInvokeDemo2();
        Parent parent = new ChildOne();
        Parent parent1 = new ChildTwo();
        demo.sayHello(parent);
        demo.sayHello(parent1);
    }
}
結果爲打印兩句parent is call。方法調用是根據變量的靜態類型確定的。

public class StaticAssignInvokeDemo {

//    public void sayHello(int a) {
//        System.out.println("int a");
//    }

    public void sayHello(long a) {
        System.out.println("long a");
    }

    public void sayHello(char a) {
        System.out.println("char a");
    }

    public void sayHello(char ... a) {
        System.out.println("char[] a");
    }

    public void sayHello(Object a) {
        System.out.println("object a");
    }

    public static void main(String[] args) {
        StaticAssignInvokeDemo demo = new StaticAssignInvokeDemo();
        demo.sayHello(1);
    }
}

字節碼如下圖:

由invokevirtual指令執行sayHello方法,它會根據參數的類型選擇最匹配的方法執行。

3.動態分配調用

       找到操作數棧頂的第一個元素所指向的對象的實際類型。如果在實際類型中找到與常量中的描述符和簡單名稱都相符的方法,則進行訪問權限校驗,如果通過則返回這個方法的直接引用,查找過程結束。如果不通過,拋出異常。按照繼承關係從下往上依次對實際類型的各父類進行搜索驗證。如果始終沒有找到,則拋出AbstractMethodError。

舉例:

public class DynamicAssignInvokeDemo {
    static class Parent {
        public void sayHello() {
            System.out.println("parent is call");
        }
    }

    static class ChildOne extends DynamicAssignInvokeDemo.Parent {
        @Override
        public void sayHello() {
            System.out.println("childOne is call");
        }
    }

    static class ChildTwo extends DynamicAssignInvokeDemo.Parent {
        @Override
        public void sayHello() {
            System.out.println("chileTwo is call");
        }
    }

    public static void main(String[] args) {
        DynamicAssignInvokeDemo.Parent parent = new DynamicAssignInvokeDemo.ChildOne();
        DynamicAssignInvokeDemo.Parent parent1 = new DynamicAssignInvokeDemo.ChildTwo();
        parent.sayHello();
        parent1.sayHello();
    }
}
執行結果爲“childOne is call”, "childTwo is call"。根據變量的實際類型確定要調用的方法。

字節碼如下圖:

invokevirtual指令在編譯期間不能確定方法的調用版本,只能在運行期間根據變量的實際類型確定調用哪個方法。

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