java方法句柄--MethodHandle

本文深入探讨Java7中引入的方法句柄API,包括MethodHandle和MethodType类的使用,以及invoke和invokeExact方法的区别。通过示例展示了如何在类间调用私有方法,解释了方法句柄在权限限制、参数类型匹配及继承调用方面的特性。

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

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

而MehodHandle对象有2个重要方法invoke和invokeExact.

MethodHandle获得的方法引用,并不能 突破访问权限本身的限制,比如private方法,就不能在类外被使用,这一点不如反射。
但是也可以暴露方法句柄给外部使用,比如下面这个例子:

就通过方法句柄,在类Study1中调用了类Test1的私有方法。

public class Test1 extends p{
    public int a=0;
    static Integer si=6;
    String s="Hello world";

    public Test1(int d){

    }

    @Override
    public void say1() {
        System.out.println("t");
    }

    private void do1(){
        a++;
        System.out.println(a);

    }
}

public class Study1 {
    public static void main(String[] args) throws Throwable {
        MethodType methodType=MethodType.methodType(Void.TYPE);


        Test1 test1=new Test1(4);


//        MethodHandle methodHandle2= MethodHandles.lookup().findVirtual(
//                Test1.class,"do1",methodType
//        );

        MethodHandle methodHandle2=Test1.getMethodHandle();
        methodHandle2.invokeExact(test1);
    }
    public void do1(){
        do2();
    }
    public void do2(){
        System.out.println("1");
    }
}

关于invoke和invokeExtract方法的区别:
invokeExtract要求更加精确,
如下 methodHandle2.invokeExact(test1,5.1,new Integer(1));可以执行,
methodHandle2.invokeExact(test1,5.1,1);会报错,因为要将1转换为integer,所以不合要求。这个方法要求不能有任何类型转换,也就是参数严格一致。
invoke相对要轻松很多。

还有一点,如果MehodHandle取得的是父类方法句柄的指针,然后调用时,第一个参数(也就是调用方)是子类的话,invokestactic会失败(也就是说它其实还要求mehodhandle所属的Class对象与 调用方实例是一个类型)。
而invoke不会做这个检查,也就是说即使句柄是父类的方法句柄,如果调用方是子类实例的话,它实际运行时会调用子类相应的覆盖的方法(推测可能是使用methodHandle在vtabl虚方法表中的索引来快速调用的)。

这个例子可以证明上面这个结论,虽然通过方法句柄拿到了方法的引用,但是如果
子类Test1中覆盖了say1方法,那么最终被调用的是子类的say1。但是很明显,方
法引用其实是指向的父类的方法。所以invoke是通过调用方的虚方法表来调用实际方法的。

而invokestaic则要求“methodHanle的调用方的Class”与“mehodHandle所引用
的method所属的Class”必须一致。


   public static void main(String[] args) throws Throwable {
        MethodType methodType=MethodType.methodType(Void.TYPE);

        MethodHandle methodHandle2= MethodHandles.lookup().findVirtual(
                p.class,"say1",methodType
        );
        Test1 test1=new Test1(4);
        methodHandle2.invoke(test1);
        }
private void do1(double b,Integer integer){
        a++;
        System.out.println("do1 id");

    }
 public static void main(String[] args) throws Throwable {
        MethodType methodType=MethodType.methodType(Void.TYPE,double.class,Integer.class);
        
        MethodHandle methodHandle2= MethodHandles.lookup().findVirtual(
                Test1.class,"do1",methodType
        );
        Test1 test1=new Test1(4);
        methodHandle2.invokeExact(test1,5.1,new Integer(1));
        methodHandle2.invokeExact(test1,5.1,1);
        }
Java中,句柄是一个重要的概念,常见的有方法句柄方法句柄是JSR 292中引入的,是对Java方法、构造方法和域的一个强类型的可执行的引用,也可视作是对方法的引用,是Java语言中一种轻量级、灵活的函数指针[^1][^2][^3]。 方法句柄的作用类似于反射中的`Method`类,但功能更强大、使用更灵活、性能更好。它使得Java语言对动态性有更好的支持,能提供更高效的方法调用方式,尤其在动态性能优化和安全访问控制方面有显著优势,还可以直接调用该句柄所引用的底层方法,并且方法句柄和反射API也可以协同使用[^1][^2][^3]。 以下是一个简单的Java方法句柄使用示例代码: ```java import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; class Example { public static int add(int a, int b) { return a + b; } } public class MethodHandleExample { public static void main(String[] args) throws Throwable { // 获取Lookup对象 MethodHandles.Lookup lookup = MethodHandles.lookup(); // 定义方法类型 MethodType methodType = MethodType.methodType(int.class, int.class, int.class); // 获取方法句柄 MethodHandle methodHandle = lookup.findStatic(Example.class, "add", methodType); // 调用方法句柄 int result = (int) methodHandle.invoke(3, 5); System.out.println("Result: " + result); } } ``` 在上述代码中,首先获取`MethodHandles.Lookup`对象,它用于查找方法句柄。然后定义方法类型`MethodType`,描述方法的返回类型和参数类型。接着使用`lookup.findStatic`方法根据类、方法名和方法类型查找静态方法句柄。最后使用`invoke`方法调用该方法句柄
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值