MethodHandle是Java7引入的一种机制,主要是为了JVM支持动态语言。

一个MethodHandle调用示例

共有方法调用

首先,演示一下最基本的MethodHandle使用。

第一步:创建查找对象:Lookup

  // 获取Look用于查找方法句柄
MethodHandles.Lookup lookup = MethodHandles.lookup();

第二步:创建方法类型:MethodType

构造方法的返回值类型,参数类型

  // 方法类型,描述返回值和参数,第一个参数为返回值类型,void则为void.class。第二个参数开始为被调用方法的参数类型
MethodType mt = MethodType.methodType(boolean.class, String.class, int.class);

第三步:查找方法句柄

Lookup的findVirtual查找成员方法

  //查找方法句柄,参数1:调用类,参数2:方法名,参数3:方法类型
MethodHandle handle = lookup.findVirtual(String.class, "startsWith", mt);

第四步:方法调用

通过MehodHandle的invoke方法执行,并返回结果

  //方法调用,参数1:实例,参数2..n:方法参数
Boolean value = (Boolean)handle.invoke("the i am in the room", "the", 0);

私有方法调用

public static void main(String..) {
Lookup myLookup = MethodHandles.lookup();
UserFactory foo = new UserFactory(); // test
User baz = new User();
try {
Method m = UserFactory.class.getDeclaredMethod("cnu", User.class);
m.setAccessible(true);
MethodHandle pmh = myLookup.unreflect(m);
boolean b = (boolean)pmh.invoke(foo, baz);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}

以上就是一个简单的调用示例。

核心代码解读

上面我们展示了一个最进本MethodHandle方式的方法调用,下面我们将对其中用到的主要类进行介绍。这些主要的类包含MethodType,MethodHandle,MethodHandles及Lookup。

MethodType

首先,我们来看看Methodtype,MethodType是用来封装方法输入输出类型的,包含方法的返回和方法的参数。MethodType构造方法为私有,只能通过MethodType提供的静态工具方法来获取实例

    /**
* Constructor that performs no copying or validation.
* Should only be called from the factory method makeImpl
*/
private MethodType(Class<?> rtype, Class<?>[] ptypes) {
this.rtype = rtype;
this.ptypes = ptypes;
}

MethodType中常用的工具方法有:

  • 大于一个参数
    public static MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
ptypes1[0] = ptype0;
System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
return makeImpl(rtype, ptypes1, true);
}
  • 无参数
    public static MethodType methodType(Class<?> rtype) {
return makeImpl(rtype, NO_PTYPES, true);
}

工具方法都通过makeImple方法来封装MethodType实例,指北君带领大家来看看makeImple中做了哪些事情:

  1. 参数检查

    • 返回值的类型不能为null,如果无返回使用void.class
    • 参数类型不能为null,且不能为void.class
  2. 使用缓存表缓存MethodType实例,优化处理

  3. 如果非信任模式(trusted==false),则克隆参数数组

这里需要注意MethodType重写了hashCode方法,从逻辑看参数数组克隆不影响同类型的缓存机制。

static
MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
if (ptypes.length == 0) {
ptypes = NO_PTYPES; trusted = true;
}
MethodType primordialMT = new MethodType(rtype, ptypes);
MethodType mt = internTable.get(primordialMT);
if (mt != null)
return mt; // promote the object to the Real Thing, and reprobe
MethodType.checkRtype(rtype);
if (trusted) {
MethodType.checkPtypes(ptypes);
mt = primordialMT;
} else {
// Make defensive copy then validate
ptypes = Arrays.copyOf(ptypes, ptypes.length);
MethodType.checkPtypes(ptypes);
mt = new MethodType(rtype, ptypes);
}
mt.form = MethodTypeForm.findForm(mt);
return internTable.add(mt);
}

除了静态工具方法外,MethodType还有几个实例方法,主要为差异性较小的MethodType提供快速获取实例的方法。包含:

  • changeParameterType
  • insertParameterTypes
  • appendParameterTypes
  • dropParameterTypes

MethodHandle

MethodHandle为抽象类,但是里面提供了大量的原生方法,提供底层访问,也是方法调用的核心逻辑。这部分涉及MethodHandle的机制实现,对于使用功能来说就不在此展开了。

    @HotSpotIntrinsicCandidate
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable; @HotSpotIntrinsicCandidate
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;

MethodHandles, Lookup

MethodHandles不是MethodHandle的实现,他提供工具用于帮助获取MethodHandle,我们主要使用到lookup(),publicLookup()

@CallerSensitive注解,可以使Reflection.getCallerClass()获取到调用lookup()方法的类

    @CallerSensitive
@ForceInline // to ensure Reflection.getCallerClass optimization
public static Lookup lookup() {
return new Lookup(Reflection.getCallerClass());
}
public static Lookup publicLookup() {
return Lookup.PUBLIC_LOOKUP;
}

Lookup构造方法主要传入两个参数:

  1. lookupClass搜寻的类,
  2. allowedModes:许可模式。

最后,通过Lookup的findXXX获取到MethodHandle,详细说明见下表:

小结

关于MethodHandle的基本使用就基本讲完,这里附上一张类图便大家理解:

【Java基础】方法调用机制——MethodHandle的更多相关文章

  1. Java方法调用机制

    最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ...

  2. Java基础——方法的调用

    Java基础--方法的调用     总结: 1. 在同一个类中-- 对于静态方法,其他的静态和非静态方法都可以直接通过"方法名"或者"类名.方法名"调用它. 对 ...

  3. 《Java基础——方法的调用》

    Java基础--方法的调用     总结: 1. 在同一个类中-- 对于静态方法,其它的静态和非静态方法都可以直接通过"方法名"或者"类名.方法名"调用它. 对 ...

  4. Java基础-方法(method)的应用

    Java基础-方法(method)的应用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 方法就是定义在类中,具有特定功能的一段小程序,方法也称为函数(function),方法可以接 ...

  5. [转]Java远程方法调用

    Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远 ...

  6. java基础:方法的定义和调用详细介绍,方法同时获取数组最大值和最小值,比较两个数组,数组交换最大最小值,附练习案列

    1. 方法概述 1.1 方法的概念 方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集 注意: 方法必须先创建才可以使用,该过程成为方法定义 方法创建后并不是直接可 ...

  7. Java基础:类加载机制

    之前的<java基础:内存模型>当中,我们大体了解了在java当中,不同类型的信息,都存放于java当中哪个部位当中,那么有了对于堆.栈.方法区.的基本理解以后,今天我们来好好剖析一下,j ...

  8. java基础之反射机制

    一.概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...

  9. java中方法调用

    JAVA中方法的调用[基础] 一.调用本类中的方法 方法一.被调用方法声明为static ,可以在其他方法中直接调用.示例代码如下: public class HelloWord { /** * @p ...

随机推荐

  1. Mac卸载go

    1.删除go目录 一般目录是 /usr/local/go sudo rm -rf /usr/local/go 2.清除环境变量配置 3. mac安装go后自动创建的问题也需要删除 sudo rm -r ...

  2. docker添加sudo权限

    sudo groupadd docker  # 添加group sudo gpasswd -a think docker  # 添加用户到组 sudo service docker restart n ...

  3. M1配置php环境完整版(用于M1芯片的Mac中,php开发环境,比如wordpress、"或wp"、emlog pro、typecho等本地开发环境的配置)

    因为macbook发布的M1是基于arm架构的,导致很多软件在短时间没无法兼容,其中包括php的很多集成开发环境软件.于是需要手动配置.网上的信息也是零七八碎,故制作了这个完整的教程. 本教程基于的m ...

  4. Java学习(八)

    今天学了类的封装知识与编译器的使用,和c++的大体一致,只有一些细节不同,像private的使用等. 小试牛刀,写了一个封装后的类,并且测试. public class Student { priva ...

  5. 【linux系统】java环境搭建

    搭建步骤 1.安装java : 上传java安装包到linux系统----- rz jdk-8u202-linux-x64.tar.gz jdk下载地址:https://www.oracle.com/ ...

  6. C代码

    #include<stdio.h>#include<stdlib.h>void main(){    char    ch, file_name1[20], file_name ...

  7. [hdu6326]Monster Hunter

    考虑树是以1为中心的菊花图的情况,也即如何安排打怪兽的顺序 用二元组$(a,b)$来描述怪兽,则对于两个怪兽$(a_{1},b_{1})$和$(a_{2},b_{2})$,交换两者不会影响血量的变化量 ...

  8. [bzoj4003]城市攻占

    倍增,对于每一个点计算他走到$2^i$次祖先所需要的攻击力以及最终会变成什么(一个一次函数),简单处理即可(然而这样是错的,因为他只保证了骑士的攻击力可以存,并没有保证这个一次函数的系数可以存)(其实 ...

  9. Java 单例模式大乱斗

    1. 饿汉模式 线程安全 public class SingleInstance1 { private static SingleInstance1 single=new SingleInstance ...

  10. 常用的分布式ID生成器

    为何需要分布式ID生成器 **本人博客网站 **IT小神 www.itxiaoshen.com **拿我们系统常用Mysql数据库来说,在之前的单体架构基本是单库结构,每个业务表的ID一般从1增,通过 ...