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指令在编译期间不能确定方法的调用版本,只能在运行期间根据变量的实际类型确定调用哪个方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值