方法调用——解析与分派

本文深入解析Java方法调用的过程,包括解析和分派阶段。静态分派依赖参数静态类型决定重载版本,而动态分派则在运行时根据实际类型确定方法重写。通过对代码示例的分析,揭示了Java多态性的本质。
摘要由CSDN通过智能技术生成

注意,方法调用并等同于方法中的代码被执行!方法调用阶段唯一做的事情就是确定被调用方法的版本。
我们知道,Class文件的编译阶段并不像其他传统语言那样会包含连接步骤,在Java语言中,连接步骤是在类的加载过程触发,因此所有的方法调用在Class文件中都只是符号引用,而不是方法在运行过程中实际的内存入口地址(直接引用)。这个特性给Java语言带来了很强大的扩展能力,可以让Java程序在运行过程中"动态"的决定调用哪个方法,但任何事情都有正反两面性,这个特性也让Java的方法调用很复杂。

解析

在类加载的解析阶段,会将一部分符号引用解析为直接引用。这种解析能够成立的前提是:方法在程序运行之前就可以确定调用的版本,并且在运行期间不会改变,即“编译器可知,运行期不可变”。这类方法的调用被称为解析”。

在字节码指令层面,方法调用的指令有以下几种:
1.invokestatic:用于调用静态方法;
2.invokespecial:用于调用实例构造器()方法、私有方法和父类中的方法;
3.invokevirtual:用于调用所有的虚方法;
4.invokeinterface:用于调用接口方法,会在运行时再确定一个实现该接口的对象;
5.invokedynamic。先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法。前面4条调用指令,分派逻辑都固化在Java虚拟机内部,而invokedynamic指令的分派逻辑是由用户设定的引导方法来决定的。关于invokedynamic的秘密我们会专门那一章节进行详细探讨。

只要能被invokestatic、invokedspecial这两个指令调用的方法,都可以在类加载的解析阶段确定其唯一的调用版本。符合这个条件的方法类型有:
静态方法:和类直接关联;
私有方法:外部不可访问;
实例构造器;
父类方法;
final修饰的方法:虽然调用final方法的指令是invokevirtual,但是final方法无法被覆盖。因此也可以在类加载的解析阶段确定其唯一版本。

分派

众所周知,Java是面向对象语言,因此Java拥有面向对象的3个基本特征:封装、继承、多态。封装和继承没啥好说的,比较简单。我们重点通过讲解方法的另一种调用方式——分派,在这个过程中揭露Java的多态本质。

静态分派

在讲解静态分派之前,我们先来看这一段代码:

package com.leon.util;

/**
 * 静态分派演示
 *
 * @author created by leon on 2020-05-15
 * @since v1.0
 */
public class StaticDispatch {
   

    static abstract class Human {
   
    }

    static class Man extends Human {
   
    }

    static class Wuman extends Human {
   
    }

    public void sayHello(Human human) {
   
        System.out.println("hello, human.");
    }

    public void sayHello(Man man) {
   
        System.out.println("hello, man");
    }

    public void sayHello(Wuman wuman) {
   
        System.out.println("hello, wuman");
    }

    public static void main(String[] args) {
   
        Human man = new Man();
        Human wuman = new 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值