【JVM 重要了解】class类文件中方法调用涉及的一些概念(简单名称、描述符、全限定名、方法句柄和方法类型、动态调用方法、符号引用)

本文详细介绍了JVM中关于类文件方法调用的相关概念,包括简单名称、全限定名、描述符、方法句柄、方法类型、动态调用方法、符号引用以及动态调用点。通过具体的示例和源码解析,帮助读者理解这些概念在Java程序执行过程中的作用和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一二中的概念如图展示:

package com.study.method;

public class TestClass {

    private int x;
    
    public int addResult() {
        return x+1;
    }
}

一、简单名称

没有类型和参数修饰的方法或字段名称
比如整形变量x和方法addResult。

二、全限定名

一个类的全限定名是将类全名的.全部替换为/
例如com/study/method/TestClass

三、描述符

如下图,这里贴上官方文档:

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

在这里插入图片描述
在这里插入图片描述
以上来自简书https://www.jianshu.com/p/8b33361a5a3f

四、方法句柄和方法类型

Java7中为间接调用方法引入了新的api,即 方法句柄。
方法句柄中包含两个重要的类,MethodHandle和MethodType.

MethodType用来描述方法的返回值类型以及入参类型。
MehodHandle包含一个指向Method对象(方法在jvm内部的对等体)的指针。

而MehodHandle对象有2个重要方法invokeinvokeExact.
invokeExtract要求更加精确

MethodHandle获得的方法引用,并不能 突破访问权限本身的限制,比如private方法,就不能在类外被使用,这一点不如反射。
通过句柄调用方法示例(来源:https://blog.youkuaiyun.com/wulianzhazha/article/details/78815647):

public class TestUtil {

    public static void main(String[] args) throws Throwable{
        //声明定义方法的签名,参数为返回值类型、参数类型
        MethodType methodType = MethodType.methodType(void.class, String.class);
        //声明定义方法句柄,通过lookup对象得到方法句柄,参数为方法所在的类、方法的名称、所匹配的方法签名
        MethodHandle methodHandle = methodHandle = MethodHandles.lookup().findVirtual(Hello.class, "sayHello", methodType);
        //调用底层方法
        methodHandle.invoke(Hello.class.newInstance(), "Test");
    }

}

class Hello {
    public void sayHello(String str) {
        System.out.println("SayHello Method.."+str);
    }
}

执行结果:
在这里插入图片描述
如果MehodHandle取得的是父类方法句柄的指针,然后调用时,第一个参数(也就是调用方)是子类的话,invokestactic会失败(也就是说它其实还要求mehodhandle所属的Class对象与 调用方实例是一个类型)。
而invoke不会做这个检查,也就是说即使句柄是父类的方法句柄,如果调用方是子类实例的话,它实际运行时会调用子类相应的覆盖的方法

五、动态调用方法

动态调用的方法:a.getClass().getMethod(str, new Class[]{}).invoke(a, new Object[]{})

a是类的对象
通过getClass()获取该类对象
getMethod(str, new Class[]{})得到的是a对象中名为str,并且不带参数的方法。如果要带参数,比如下面的String 和 int,那就是getMethod(str, new Class[]{String.class,int.class})
invoke(a,new Object[]{})调用方法,第一个参数是要调用这个方法的对象,如果方法是static的,这个参数可以为null

如果调用有参数的方法str(String s, int i),应该这样写invoke(a,new Object[]{“content”, 1})

来源(https://blog.youkuaiyun.com/jiangeeq/article/details/78899797

public class TestUtil {
    private static TestUtil movingInvokeTest = new TestUtil();


    public void do_test(String str,int i) throws Exception {
        if(i == 0)
        movingInvokeTest.getClass().getMethod(str, new Class[]{}).invoke(movingInvokeTest, new Object[]{});
      else if(i == 1)
        movingInvokeTest.getClass().getMethod(str, new Class[]{String.class}).invoke(movingInvokeTest, new Object[]{"s"});
      else if(i == 2)
        movingInvokeTest.getClass().getMethod(str, new Class[]{String.class,int.class}).invoke(movingInvokeTest, new Object[]{"qw",1});
    }

    /*

     * 下面是3个方法

     */

    public void speak()

    {

        System.out.println("调用的没有参数的方法");

    }
    public void speak(String s)
    {
        System.out.println("调用有一个参数的方法,参数为:"+s);
    }
    public void speak(String s,int i)
    {
        System.out.println("调用有两个参数的方法,参数为,参数为:"+s+"和" +i);
    }


    public static void main(String[] args) throws Exception
    {

        movingInvokeTest.do_test("speak",1);
    }

}

不同输入的测试结果,分别是i为0,1,2时的结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、符号引用

在这里插入图片描述
比如这一句
ps.println(“hello world”); //ps为PrintStream对象
Java语言在编译期间就已将println(String)方法完整的符号引用(本例中为一项CONSTANT_InterfaceMethodref_info常量)生成出来
在这里插入图片描述
如上,当作为方法调用的参数存储到class文件中时,就是这个格式。
在JDK7之前的字节码指令集中,方法调用指令的第一个参数都是被调用方法的符号引用

七、动态调用点

涉及 invokedynamic指令,篇幅较长,这里不多作解释。
每一处含有invokedynamic指令的位置都被称作“动态调用点(Dynamically-Computed Call Site)”,这条指令的第一个参数不再是代表方法符号引用的CONSTANT_Methodref_info常量,而是变为JDK 7时新加入的CONSTANT_InvokeDynamic_info常量,从这个新常量中可以得到3项信息:引导方法(Bootstrap Method,该方法存放在新增的BootstrapMethods属性中)、方法类型(MethodType)和名称。引导方法是有固定的参数,并且返回值规定是java.lang.invoke.CallSite对象,这个对象代表了真正要执行的目标方法调用。根据CONSTANT_InvokeDynamic_info常量中提供的信息,虚拟机可以找到并且执行引导方法,从而获得一个CallSite对象,最终调用到要执行的目标方法上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值