目录

一、编程语言类型

1、解释型与编译型语言

2、静态类型语言与动态类型语言

3、强类型语言和弱类型语言

4、动态语言和静态语言

二、java.lang.invoke

1、JSR-292

2、MethodType

3、MethodHandles.Lookup

4、MethodHandle

5、MethodHandles

三、与Java Reflect的区别


在上一篇《Hotspot JNI和字节码方法调用源码解析》中讲到了 invokedynamic动态方法调用指令,从《Java虚拟机规范》中invokedynamic的描述可知invokedynamic的底层实现是基于java.lang.invoke.MethodHandle的,下面来详细探讨下MethodHandle的用法以及同Java Reflect的差异对比。

一、编程语言类型

1、解释型与编译型语言

解释型语言:是指通过解释器解释执行的语言,解释型语言不需要编译成与底层硬件平台直接交互的机器码语言,只要能被解释器识别即可,可以是源码或者类似于字节码的中间代码,典型的如JavaScript,Python,对应的解释器是谷歌V8 JavaScript执行引擎引入的解释器 Ignition,官方Python解释器CPython。

编译型语言:是指可以通过编译器编译变成可以直接在底层硬件平台上直接运行的机器码,可以与底层硬件直接交互的语言,典型的如C/C++,C-object,操作系统如Linux,windows等,大小硬件设备的驱动程序基本都是用C写的。

解释型语言因为必须经过中间的解释器解释执行,所以性能相对而言要差,但是语法规则可以更灵活,且因为解释器屏蔽了同底层硬件交互的细节,解释型语言学习成本更低,跨平台能力更强。编译型语言必须与底层硬件平台直接交互,能够基于底层平台特性做特定优化,所以性能更好,也是因此需要综合考虑并兼顾不同底层硬件平台的特性,代码更加复杂,学习成本高。

Java可以通过默认的字节码模板解释器解释执行,也可以通过JIT即时编译器编译执行,通过-X+int选项指定以解释方式执行,通过-Xcomp选项指定以编译方式执行,通过-Xmixed,以解释+热点代码编译的方式执行,默认是-Xmixed。

2、静态类型语言与动态类型语言

静态类型语言:是指对数据类型/方法调用的检查是在编译期执行的,根据变量声明的类型来检查,典型的如Java

动态类型语言:是指对数据类型/方法调用的检查是在运行期执行的,根据变量实际的值类型来检查,典型的如JavaScript

Java的测试用例如下:

public class TypeTest {public static void test(String s){System.out.println("test:"+s);}public static void main(String[] args) {int a=12;TypeTest.test(a);}
}

执行javac编译报错,如下图:

如果是方法调用如TypeTest.test(a);会检查TypeTest类是否包含静态test方法。

JavaScript的测试用例如下:

function add(a,b){return a+b;
}console.log(add(1,2));console.log(add("a",2));

执行结果如下:

声明add方法时不需要声明变量a和变量b的类型,没有声明肯定就不会在编译时校验类型了,解释执行的时候会根据变量a和变量b的实际类型决定a+b的结果,如果两个都是数字则a+b表示两个数字相加,如果其中一个表示字符串则a+b表示将a和b以字串符的形式连接起来。

3、强类型语言和弱类型语言

强类型语言是指一个变量如果被声明成什么类型了,在整个运行期该变量就一直是该类型,除非被强转成其他数据类型,典型的如Java

弱类型语言是指一个变量在声明时不需要指定特定的类型,而是可以在运行时赋值成任一数据类型的值,典型的如JavaScipt。

Java的测试用例如下:

public class TypeTest {public static void main(String[] args) {int a=12;long b=13L;a=b;}
}

javac编译报错:

上述代码如果改成a=(int)b就不会报错,即将b强转成int赋值给a,b的类型依然是long。

javaScript的测试用例如下:

var a=12;console.log(a);a="test";console.log(a);

执行结果如下:

变量a先是被赋值成整数类型,接着又被赋值成字符串类型,都可以正常执行。

4、动态语言和静态语言

动态语言:指在运行时可以改变其结构的语言,如新的属性、函数、对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化,典型的如JavaScript

静态语言:指运行时结构不可变的语言,典型的如Java

JavaScript的测试用例如下:

class cla{};cla.a=1;console.log(cla.a);cla.test=function(){console.log("test");};cla.test();

代码执行结果如下:

a和test不属于cla中定义的属性,而是在运行时动态添加的

Java测试用例如下:

public class TypeTest {public int a;public static void main(String[] args) {TypeTest typeTest=new TypeTest();typeTest.a=1;typeTest.b=2;}
}

javac编译报错,如下图:

属性a是TypeTest中声明的所以typeTest.a=1;没有报错,属性b不是TypeTest中声明的,所以报错找不到符号。

参考:编译型与解释型、动态语言与静态语言、强类型语言与弱类型语言的区别

二、java.lang.invoke

1、JSR-292

java.lang.invoke是Java7实现JSR-292而引入的,为了在缺乏静态类型信息,运行时才能获取类型信息的情况下能够高效和灵活的执行方法调用,即在方法调用层面上提供对动态类型语言的支持,如执行obj.println("hello world"); 不再要求obj必须是java.io.PrintStream类型,只要在运行期obj定义了println方法即可。

因为之前定义的四个方法调用指令invokevirtual invokespecial invokestatic invokeinterface的第一个参数都是被调用方法的符号引用,根据符号引用可以解析出被调用方法的接收对象的类型和方法定义,即这四个指令要求在编译期必须确认被调用方法的接收对象的对象类型,如编译obj.println("hello world"); 必须确认obj的具体的类型,然后检查该类型是否定义了println方法,检查通过再据此生成一个被调用方法的符号引用。为了能够在JVM字节码指令层面实现在运行期才确认被调用方法的接收对象的类型,JVM配套的引入了一个新的方法调用指令invokedynamic,该指令同时也是Java8实现lamada的技术基础。注意java.lang.invoke包的底层实现并没有依赖invokedynamic指令,在非lamada下javac目前也不会生成invokedynamic指令,invokedynamic指令主要给同样是JVM上运行的动态类型语言使用,如Groovy。

测试用例如下:

package jni;import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.List;class TestA{public void println(String s){System.out.println("TestA println:"+s);}
}class TestB{public void println(String s){System.out.println("TestB println:"+s);}
}public class MethodHandlerTest {public static void lamadaTest(){List<String> s= Arrays.asList("a","b","c");s.stream().forEach((String str) ->{System.out.println(str);});}public static void println(Object obj,String s) throws Throwable{MethodType mt=MethodType.methodType(void.class,String.class);MethodHandle methodHandle=MethodHandles.lookup().findVirtual(obj.getClass(),"println",mt);methodHandle.bindTo(obj).invokeExact(s);}public static void main(String[] args) throws Throwable{String test="Hello World";println(new TestA(),test);println(new TestB(),test);println(System.out,test);lamadaTest();}
}

上述用例中执行println方法时,obj的具体类型是不确定的,是在运行时才确认的,执行结果如下:

通过javap -v可以查看MethodHandlerTest的字节码,如下:

public static void lamadaTest();descriptor: ()Vflags: ACC_PUBLIC, ACC_STATICCode:stack=4, locals=1, args_size=00: iconst_31: anewarray     #2                  // class java/lang/String4: dup5: iconst_06: ldc           #3                  // String a8: aastore9: dup10: iconst_111: ldc           #4                  // String b13: aastore14: dup15: iconst_216: ldc           #5                  // String c18: aastore19: invokestatic  #6                  // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;22: astore_023: aload_024: invokeinterface #7,  1            // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;29: invokedynamic #8,  0              // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;34: invokeinterface #9,  2            // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V39: returnLineNumberTable:line 24: 0line 25: 23line 26: 39LocalVariableTable:Start  Length  Slot  Name   Signature23      17     0     s   Ljava/util/List;LocalVariableTypeTable:Start  Length  Slot  Name   Signature23      17     0     s   Ljava/util/List<Ljava/lang/String;>;public static void println(java.lang.Object, java.lang.String) throws java.lang.Throwable;descriptor: (Ljava/lang/Object;Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=4, locals=4, args_size=20: getstatic     #2                  // Field java/lang/Void.TYPE:Ljava/lang/Class;3: ldc           #3                  // class java/lang/String5: invokestatic  #4                  // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;8: astore_29: invokestatic  #5                  // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;12: aload_013: invokevirtual #6                  // Method java/lang/Object.getClass:()Ljava/lang/Class;16: ldc           #7                  // String println18: aload_219: invokevirtual #8                  // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;22: astore_323: aload_324: aload_025: invokevirtual #9                  // Method java/lang/invoke/MethodHandle.bindTo:(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;28: aload_129: invokevirtual #10                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;)V32: returnLineNumberTable:line 22: 0line 23: 9line 24: 23line 25: 32LocalVariableTable:Start  Length  Slot  Name   Signature0      33     0   obj   Ljava/lang/Object;0      33     1     s   Ljava/lang/String;9      24     2    mt   Ljava/lang/invoke/MethodType;23      10     3 methodHandle   Ljava/lang/invoke/MethodHandle;Exceptions:throws java.lang.Throwablepublic static void main(java.lang.String[]) throws java.lang.Throwable;descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=10: ldc           #18                 // String Hello World2: astore_13: new           #19                 // class jni/TestA6: dup7: invokespecial #20                 // Method jni/TestA."<init>":()V10: aload_111: invokestatic  #21                 // Method println:(Ljava/lang/Object;Ljava/lang/String;)V14: new           #22                 // class jni/TestB17: dup18: invokespecial #23                 // Method jni/TestB."<init>":()V21: aload_122: invokestatic  #21                 // Method println:(Ljava/lang/Object;Ljava/lang/String;)V25: getstatic     #24                 // Field java/lang/System.out:Ljava/io/PrintStream;28: aload_129: invokestatic  #21                 // Method println:(Ljava/lang/Object;Ljava/lang/String;)V32: invokestatic  #25                 // Method lamadaTest:()V35: returnLineNumberTable:line 35: 0line 36: 3line 37: 14line 38: 25line 40: 32line 41: 35LocalVariableTable:Start  Length  Slot  Name   Signature0      36     0  args   [Ljava/lang/String;3      33     1  test   Ljava/lang/String;Exceptions:throws java.lang.Throwable

从上述字节码可知java.lang.invoke包的相关方法调用并没有使用invokedynamic指令,lamada语句使用了invokedynamic指令。

2、MethodType

MethodType用来表示方法的入参类型和返回值类型,相当于期望的被调用方法的方法描述符,在执行MethodHandle#invokeExact方法、MethodHandle#invoke方法或者invokedynamic指令时,JVM会检查被调用方法与MethodType是否匹配。

一个MethodType由一个返回类型和任意多个参数类型组成,每个类型都是Class,如基本类型int.class,对象类型String.class,无返回值void.class。

所有的MethodType实例同String实例一样都是不可变的,当两个MethodType实例的返回类型和参数类型都相同则认为它们相等。MethodType只能通过工厂方法创建,传递参数类型时可以通过数组或者Listc传递。

MethodType可以通过方法描述符创建,这时方法描述符中涉及的类就必须已经完成加载,但是可以不初始化。

MethodType提供的方法可以分为以下几类:

1)工厂方法

其中genericMethodType方法是针对方法的返回值和参数类型都是Object时的methodType(java.lang.Class, java.lang.Class[])的简便方法。

2)修改返回参数类型和方法参数类型的方法

因为MethodType是不可变的,所以上述方法实际是在当前MethodType的基础上做适当修改然后构造了一个新的MethodType实例。

3)读取返回参数类型和方法参数类型的方法

其中 hasPrimitives方法判断MethodType的参数类型和返回类型中是否包含基本类型,hasWrappers判断是否包含包装器类型,如Interger。

4)对当前的MethodType进行转换的便利方法

erase方法将MethodType的参数类型和返回类型中包含的引用类型全部替换成Object.class

generic方法将MethodType的参数类型和返回类型中包含的所有类型全部替换成Object.class

wrap()方法将MethodType的参数类型和返回类型中包含的基本类型全部替换成对应的包装器类型

unwrap()方法将MethodType的参数类型和返回类型中包含的包装器类型全部替换成对应的基本类型

5)跟方法描述符字符串来回转换的方法:

3、MethodHandles.Lookup

Lookup类是MethodHandles的内部类,用public static final 修饰。Lookup就是一个创建MethodHandler的工厂类,Lookup在只在创建MethodHandler时检查执行lookup的类对目标方法的访问权限,而不是在通过MethodHandler调用特定方法时检查,Java反射API会每次调用都需要检查权限,这是Lookup与Java反射API最关键的不同。执行MethodHandles#lookup方法的类的Class作为构造方法参数构造Lookup实例,可以通过lookupClass()方法获取该Class,称之为lookup class。构造的Lookup实例也可以被共享,其他代码使用共享的Lookup实例时,依然使用创建Lookup实例时的类的Class来检查访问权限而非当前使用Lookup实例的类的Class。

Lookup类提供了多个用于创建操作构造方法,普通方法和字段的的MethodHandle的工厂方法,这些方法创建的MethodHandle的底层操作和对应字节码的底层操作是基本等同的,如下表。少数情况下不等同,参考MethodHandles.Lookup的注释说明。

表中C表示执行lookup的目标类,FT表示目标字段的类型,MT表示目标方法的MethodType,aMethod,aField,aConstructor表示反射中的Method,Field,Constructor实例;表中最后一个unreflect方法应该是unreflectSpecial,官方注释有误。比如执行Lookup#findGetter方法获取MethodHandle,通过该MethodHandle获取某个类FT的属性f,其底层操作上相当于this.f对应的字节码getfiled。表中的findSpecial方法需要重点关注,findSpecial方法要求最后一个参数Class<?> specialCaller与lookup 实例保存的lookup class一样且lookup实例保存的lookup class具有访问第一个参数Class<?> refc类的私有成员的权限,这样做的目的是将lookup能够访问的私有成员限制在lookup class能够访问的私有成员范围内。

除上述方法外,Lookup类还包含如下方法:

  • int lookupModes():获取Lookup实例能够访问的成员类型,其结果是取Lookup实例的allowedModes属性同PACKAGE (0x08)的并运算,allowedModes只能是PUBLIC (0x01), PRIVATE (0x02), PROTECTED (0x04),ALL_MODES
    四个掩码之一。
  • MethodHandles.Lookup in(Class<?> requestedLookupClass):用一个新的lookupClass创建一个新的Lookup实例,但是新实例能够访问的成员比原来的少。
  • MethodHandle bind(Object receiver,String name,MethodType type):从receiver对应的Class中查找目标方法,如果存在且当前Lookup实例通过findVirtual可以访问该方法,则将receiver绑定到Lookup实例创建的MethodHandle上。

测试用例如下:

import java.lang.invoke.MethodHandles;interface interfaceTest{void interTest();
}class superA{public void println(String s){System.out.println("superA println:"+s);}}public class TestB extends superA implements interfaceTest{public int a;public static int b;private int c;public TestB(){this.a=11;b=12;c=13;};TestB(int a,String s){System.out.println("a="+a+",s="+s);this.a=21;b=22;c=23;}public void println(String s){System.out.println("TestB println:"+s);}private void privateTest(String s){System.out.println("TestB privateTest:"+s);}public static void staticPrintln(String s){System.out.println("TestB staticPrintln:"+s);}public static MethodHandles.Lookup lookup(){return MethodHandles.lookup();}@Overridepublic void interTest() {System.out.println("TestB interTest");}@Overridepublic String toString() {return "TestB{" +"a=" + a +",b=" + b +",c=" + c +'}';}
}
import org.junit.Test;import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class LookupTest{@Testpublic void testField() throws Throwable {TestB testB=new TestB();MethodHandles.Lookup lookup=MethodHandles.lookup();MethodHandle setter=lookup.findSetter(TestB.class,"a",int.class);setter.bindTo(testB).invoke(2);MethodHandle staticSetter=lookup.findStaticSetter(TestB.class,"b",int.class);staticSetter.invoke(3);System.out.println("setter result->"+testB);MethodHandle getter=lookup.findGetter(TestB.class,"a",int.class);int a=(int)getter.bindTo(testB).invoke();MethodHandle staticGetter=lookup.findStaticGetter(TestB.class,"b",int.class);int b=(int)staticGetter.invoke();System.out.println("getter result TestB a="+a+",b="+b);Field aField=TestB.class.getField("a");aField.setInt(testB,4);Field bField=TestB.class.getField("b");bField.setInt(null,5);System.out.println("reflect setter result->"+testB);a=aField.getInt(testB);b=bField.getInt(null);System.out.println("reflect getter result TestB a="+a+",b="+b);//无法访问私有属性,报错member is private: TestB.c/int/putField, from MethodHandleTest
//        MethodHandle privateSetter=lookup.findSetter(TestB.class,"c",int.class);//返回的lookup实例的lookup class就是TestB.class,所以可以访问私有成员lookup=TestB.lookup();MethodHandle privateSetter=lookup.findSetter(TestB.class,"c",int.class);privateSetter.bindTo(testB).invoke(6);System.out.println("findSetter private->"+testB);//无法访问私有属性,报错java.lang.NoSuc
//        Field cField=TestB.class.getField("c");Field cField=TestB.class.getDeclaredField("c");//必须执行setAccessible(true); 否则报错Class MethodHandleTest can not access a member of class TestB with modifiers "private"cField.setAccessible(true);cField.setInt(testB,7);System.out.println(testB);int c=cField.getInt(testB);System.out.println("private get c="+c);}@Testpublic void constuctTest() throws Throwable{MethodHandles.Lookup lookup=MethodHandles.lookup();//构造函数的返回类型是voidMethodHandle defaultConstructor=lookup.findConstructor(TestB.class, MethodType.methodType(void.class));TestB testB=(TestB) defaultConstructor.invoke();System.out.println("defaultConstructor:"+testB);MethodHandle paramConstructor=lookup.findConstructor(TestB.class, MethodType.methodType(void.class,int.class,String.class));testB=(TestB) paramConstructor.invoke(1,"test");System.out.println("param constructor:"+testB);Constructor defaultConstructor2=TestB.class.getConstructor();testB=(TestB) defaultConstructor2.newInstance();System.out.println("reflect defaultConstructor:"+testB);testB=TestB.class.newInstance();System.out.println("reflect newInstance:"+testB);//getConstructor是查找公开的构造方法,此构造方法是默认的包级访问,所以getConstructor报错
//        Constructor paramConstructor2=TestB.class.getConstructor(int.class,String.class);//getDeclaredConstructor是获取声明的构造方法,获取的Constructor执行newInstance方法时会校验当前类是否//具备访问权限,如果没有权限需要执行setAccessible(true);Constructor paramConstructor2=TestB.class.getDeclaredConstructor(int.class,String.class);testB=(TestB) paramConstructor2.newInstance(7,"ts");System.out.println("reflect paramConstructor2:"+testB);}@Testpublic void methodTest() throws Throwable {MethodHandles.Lookup lookup=MethodHandles.lookup();MethodHandle staticHandle=lookup.findStatic(TestB.class,"staticPrintln",MethodType.methodType(void.class,String.class));staticHandle.invoke("static test");//方法没有入参,则不用传任何类型MethodHandle interfaceHandle=lookup.findVirtual(interfaceTest.class,"interTest",MethodType.methodType(void.class));interfaceHandle.bindTo(new TestB()).invoke();MethodHandle virtualHandle=lookup.findVirtual(superA.class,"println",MethodType.methodType(void.class,String.class));// 如果使用TestB.class,则执行bindTo(new superA())时java.lang.ClassCastException: Cannot cast superA to TestB
//        MethodHandle virtualHandle=lookup.findVirtual(TestB.class,"println",MethodType.methodType(void.class,String.class));virtualHandle.bindTo(new superA()).invoke("super test");virtualHandle.bindTo(new TestB()).invoke("sub test");//findSpecial方法要求第四个参数specialCaller和lookup class一致或者lookup class可以访问私有成员,即必须在目标类中创建lookup//否则报错no private access for invokespecial: class TestB, from MethodHandleTest//可以通过findSpecial调用私有方法,父类方法
//        MethodHandle specialHandle=lookup.findSpecial(TestB.class,"println",MethodType.methodType(void.class,String.class),TestB.class);lookup=TestB.lookup();MethodHandle specialHandle=lookup.findSpecial(TestB.class,"println",MethodType.methodType(void.class,String.class),TestB.class);specialHandle.bindTo(new TestB()).invoke("findSpecial test");specialHandle=lookup.findSpecial(superA.class,"println",MethodType.methodType(void.class,String.class),TestB.class);specialHandle.bindTo(new TestB()).invoke("findSpecial test");//调用私有方法specialHandle=lookup.findSpecial(TestB.class,"privateTest",MethodType.methodType(void.class,String.class),TestB.class);specialHandle.bindTo(new TestB()).invoke("findSpecial privateTest");Method method=superA.class.getMethod("println",String.class);//如果是TestB.class,则执行invoke new superA()时报错object is not an instance of declaring class
//        Method method=TestB.class.getMethod("println",String.class);method.invoke(new superA(),"reflect super test");method.invoke(new TestB(),"reflect sub test");method=TestB.class.getDeclaredMethod("privateTest",String.class);method.setAccessible(true);method.invoke(new TestB(),"reflect private test");}@Testpublic void reflectTest() throws Throwable {MethodHandles.Lookup lookup=MethodHandles.lookup();TestB testB=new TestB();Field aField=TestB.class.getField("a");MethodHandle setter=lookup.unreflectSetter(aField);setter.bindTo(testB).invoke(22);System.out.println(testB);MethodHandle getter=lookup.unreflectGetter(aField);int a=(int)getter.bindTo(testB).invoke();System.out.println("testB a="+a);Method method=superA.class.getMethod("println",String.class);MethodHandle methodHandle=lookup.unreflect(method);methodHandle.bindTo(testB).invoke("unreflect test");//报错no private access for invokespecial: class TestB, from MethodHandleTest
//        MethodHandle specialMethodHandle=lookup.unreflectSpecial(method,TestB.class);MethodHandle specialMethodHandle=TestB.lookup().unreflectSpecial(method,TestB.class);//调用TestB中println方法的父类实现specialMethodHandle.bindTo(testB).invoke("unreflectSpecial test");Constructor paramConstructor=TestB.class.getDeclaredConstructor(int.class,String.class);MethodHandle constuctorMethodHandle=lookup.unreflectConstructor(paramConstructor);TestB testB2=(TestB) constuctorMethodHandle.invoke(33,"unreflectConstructor test");System.out.println(testB2);}
}

4、MethodHandle

一个MethodHandle实例表示一个可以直接执行的对普通方法,构造方法,字段等操作或者其他的低级别的操作的引用。MethodHandle是跟方法的返回值类型和参数类型强相关的,它们并不是根据方法名或者定义方法的类来区分的,通过MethodHandle调用某个方法时要求目标方法的描述符必须与MethodHandle的类型描述符(即关联的MethodType实例)一致,可通过MethodHandle#type()方法获取关联的MethodType实例,MethodType实例决定了MethodHandler支持调用的方法类型。

MethodHandle有两个执行具体方法调用的方法 invokeExact和invoke,invokeExact要求被调用方法的描述符与MethodHandle完全一致,而invoke则允许适当的不一致,如果一致其执行速度和invokeExact一样,如果不一致invoke会尝试通过MethodHandle#asType(MethodType newType)方法创建一个跟目标方法一致的MethodType,然后利用该MethodType执行invokeExact方法。但是不能保证asType会被调用,如果JVM可以自动适配目标方法的参数就会直接调用了。

MethodHandle实例本身是不可变的,也没有任何可见的状态,相当于一个final变量。MethodHandle是一个抽象类但是不建议开发者实现一个子类,因为MethodHandler的类继承体系未来可能随着时间的改变而改变。

MethodHandle提供的方法可以分为以下几类:

1)方法调用:

Object invokeExact(Object... args):要求执行调用时的参数类型与返回值类型与MethodHandle关联的MethodType完全一致
Object invoke(Object... args):跟invokeExact相比,不要求完全一致,invoke会通过判断执行调用时的参数类型与返回值类型能否转换成MethodHandle关联的MethodType,如果能转换则通过MethodHandle asType(MethodType newType)方法生成一个新的MethodHandle,对新的MethodHandle执行invokeExact方法。
MethodHandle bindTo(Object x):将x作为invoke或者invokeExact的第一个参数,即方法调用的接收对象,如果没有执行该方法,则invoke或者invokeExact将第一个参数作为方法调用的接收对象。
Object invokeWithArguments(Object... arguments) :执行方法调用,要求方法参数是多个跟arguments个数一致的Object
Object invokeWithArguments(java.util.List<?> arguments),同上

2)MethodHandler转换

MethodHandle asType(MethodType newType) :用新的MethodType 创建一个新的MethodHandle
MethodHandle asSpreader(Class<?> arrayType, int arrayLength): 支持将调用参数中的数组的元素转换成方法入参
MethodHandle asCollector(Class<?> arrayType, int arrayLength): 将可变参数个数的MethodHandle转换成固定参数个数的
MethodHandle asVarargsCollector(Class<?> arrayType):将数组形式的入参转换成支持可变参数个数的MethodHandle
MethodHandle asFixedArity() :将参数类型固定,不允许invoke调用时做类型转换

测试用例如下:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
import java.util.Arrays;
import java.util.List;import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.Assert.assertEquals;public class MethodHandleTest {@Testpublic void invokeTest() throws Throwable {MethodHandles.Lookup lookup=MethodHandles.lookup();MethodHandle methodHandle=lookup.findStatic(TestB.class,"staticPrintln", methodType(void.class,Object.class));//invoke内部会调用asType产生一个适配的MethodType,不过能够适配的类型非常有限//能够适配的逻辑参考MethodType#canConvert方法methodHandle.invoke(122);//invokeExact要求类型严格匹配,test是String不是Object类型
//        methodHandle.invokeExact("test");MethodType newType=methodHandle.type().changeParameterType(0,String.class);methodHandle=methodHandle.asType(newType);methodHandle.invokeExact("test");}@Testpublic void asVarargsCollectorTest() throws Throwable {MethodHandles.Lookup lookup=MethodHandles.lookup();//Arrays#deepToString方法本来的参数类型是Object[],不是不可变参数类型MethodHandle methodHandle=lookup.findStatic(Arrays.class,"deepToString", methodType(String.class,Object[].class));//返回false,表示不支持可变参数System.out.println(methodHandle.isVarargsCollector());//调用报错cannot convert MethodHandle(Object[])String to (int,int,int,int)List
//        String result=(String)methodHandle.invoke(1,2,3,4);String result=(String)methodHandle.invoke(new Object[]{1,2,3,4});System.out.println(result);//asVarargsCollector将其转换成支持可变参数个数的MethodHandle//如果MethodHandle对应的方法本身就支持可变参数则不做任何转换methodHandle=methodHandle.asVarargsCollector(Object[].class);result=(String)methodHandle.invoke(1,2,3,4);System.out.println(result);result=(String)methodHandle.invoke(new Object[]{11,12,13,14});System.out.println(result);methodHandle=lookup.findStatic(Arrays.class,"asList", methodType(List.class,Object[].class));System.out.println(methodHandle.isVarargsCollector());List list=(List) methodHandle.invoke(21,22,23,24);System.out.println(list);}@Testpublic void asCollectorTest() throws Throwable {MethodHandles.Lookup lookup=MethodHandles.lookup();MethodHandle methodHandle=lookup.findStatic(Arrays.class,"deepToString", methodType(String.class,Object[].class));methodHandle=methodHandle.asCollector(Object[].class,3);System.out.println(methodHandle.type().equals(methodType(String.class,Object.class,Object.class,Object.class)));//将原来的Object数组变成指定参数个数,如果个数不符报错cannot convert MethodHandle(Object,Object,Object)String to (String,String)String
//        String result=(String)methodHandle.invoke("a","b");
//        new Object[]被当成一个对象,报错cannot convert MethodHandle(Object,Object,Object)String to (Object[])String
//        String result=(String)methodHandle.invoke(new Object[]{"a","b","c"});String result=(String)methodHandle.invoke("a","b","c");System.out.println(result);System.out.println(methodHandle.isVarargsCollector());}@Testpublic void asSpreaderTest() throws Throwable {MethodHandle equals = publicLookup().findVirtual(String.class, "equals", methodType(boolean.class, Object.class));assert((boolean)equals.bindTo("me").invokeExact((Object) "me"));assert(!(boolean)equals.bindTo("me").invokeExact((Object)"thee"));assert( (boolean) equals.invokeExact("me", (Object)"me"));assert(!(boolean) equals.invokeExact("me", (Object)"thee"));MethodHandle eq2 = equals.asSpreader(Object[].class, 2);assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));MethodHandle eq1 = equals.asSpreader(Object[].class, 1);assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));//toString方法的参数类型都是数组MethodHandle caToString = publicLookup().findStatic(Arrays.class, "toString", methodType(String.class, char[].class));assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));//限定方法的参数个数是3MethodHandle caString3 = caToString.asCollector(char[].class, 3);assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));//会把数组元素转换成3个参数MethodHandle caToString3 = caString3.asSpreader(char[].class, 3);assertEquals("[A, B, C]", (String) caToString3.invokeExact("ABC".toCharArray()));//报错数组长度不对 array is not of length 3
//        assertEquals("[A, B, C]", (String) caToString3.invokeExact("ABCD".toCharArray()));//会把数组元素转换成2个参数MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));//会把数组元素转换成1个参数MethodHandle caToString1 = caString3.asSpreader(char[].class, 1);assertEquals("[A, B, C]", (String) caToString1.invokeExact('A','B',"C".toCharArray()));}@Testpublic void asFixedArityTest() throws Throwable {MethodHandle asListVar = publicLookup().findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)).asVarargsCollector(Object[].class);//固定参数类型,即不能自动转换MethodHandle asListFix = asListVar.asFixedArity();assertEquals("[1]", asListVar.invoke(1).toString());Exception caught = null;try {//报错Cannot cast java.lang.Integer to [Ljava.lang.Object;asListFix.invoke((Object)1);} catch (Exception ex) {caught = ex;ex.printStackTrace();}assert(caught instanceof ClassCastException);assertEquals("[two, too]", asListVar.invoke("two", "too").toString());try {//报错cannot convert MethodHandle(Object[])List to (String,String)voidasListFix.invoke("two", "too");} catch (Exception ex) {caught = ex;ex.printStackTrace();}assert(caught instanceof WrongMethodTypeException);Object[] argv = { "three", "thee", "tee" };assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());//必须使用Object[]类型assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());//将Object[]类型转换成Object时,因为实际类型是Object[]所以没报错assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());}@Testpublic void invokeWithArgumentsTest() throws Throwable {MethodHandles.Lookup lookup=MethodHandles.lookup();MethodHandle methodHandle=lookup.findStatic(Arrays.class,"deepToString", methodType(String.class,Object[].class)).asCollector(Object[].class,3);//调用参数类型是Object,Object,Object的方法String result=(String)methodHandle.invokeWithArguments(1,2,3);System.out.println(result);result=(String)methodHandle.invokeWithArguments(Arrays.asList(4,5,6));System.out.println(result);//报错expected (Object,Object,Object)String but found (int,int,int)String
//        result=(String)methodHandle.invokeExact(1,2,3);result=(String)methodHandle.invoke(1,2,3);System.out.println(result);}}

5、MethodHandles

MethodHandles包含一系列的操作MethodHandler的静态方法,可以分为以下3类:

1)创建Lookup实例

Lookup lookup():该方法返回的Lookup实例可以访问所有的类成员,包括私有成员,不过前提是lookup class有访问权限

Lookup publicLookup():该方法返回的Lookup实例只能访问公开的类成员,该实例的lookup class是Object.class

2)数组操作

MethodHandle arrayElementGetter(Class<?> arrayClass):获取读取数组元素的MethodHandle
MethodHandle arrayElementSetter(Class<?> arrayClass):获取修改数组元素的MethodHandle

3)invoker操作

MethodHandle invoker(MethodType type):相当于执行publicLookup().findVirtual(MethodHandle.class, "invoke", type),获取的MethodHandle可以将MethodHandle实例作为接受对象,调用MethodHandle实例绑定的方法,不过调用时需要通过invokeWithArguments方法调用
MethodHandle exactInvoker(MethodType type):相当于执行publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
MethodHandle spreadInvoker(MethodType type, int leadingArgCount):相当于执行如下代码:

4)MethodHandle参数处理

MethodHandle explicitCastArguments(MethodHandle target, MethodType newType):将MethodHandle的MethodType转换成新的newType,相当于MethodHandle#asType()方法,不过转换限制更少。
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder):调整MethodHandle的入参顺序,比如有三个参数,正常按照0,1,2的顺序传参,如果order位2,0,1则invoke调用时第一个入参会放到方法入参的第三个参数,第二个参数放到方法入参的第一个参数,第三个参数放到方法入参的第二个参数。
MethodHandle insertArguments(MethodHandle target, int pos, Object... values):将从索引pos开始的方法入参的值固定为后面的values,invoke时只需要传其他的非固定值的参数即可
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes):从入参索引pos开始丢弃valueTypes类型的参数,剩下的参数用来调用target。
MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes); 同上
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters):参数的预处理,从入参索引pos开始的参数都执行一遍后面的filter,执行结果作为新的入参调用target
MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter):同上参数预处理,预处理结果作为新的入参调用target,不过支持嵌套调用
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner):同上参数预处理,不过combiner预处理的结果是作为新的入参插入到原来的入参的前面,最后用这些参数执行target
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter):先执行target,执行结果作为filter的入参,执行filter的结果作为最后的结果

5)MethodHandle条件调用

MethodHandle guardWithTest(MethodHandle test,MethodHandle target,MethodHandle fallback):先执行test,如果test返回true,则执行target,否则执行fallback
MethodHandle catchException(MethodHandle target,Class<? extends Throwable> exType,MethodHandle handler) :执行target,如果抛出异常exType,则执行handler

6)特殊的MethodHandle
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType):返回一个将指定类型exType的异常作为入参并抛出该异常的MethodHandle,returnType非空但无意义
MethodHandle identity(Class<?> type):返回一个接受指定类型的一个参数的MethodHandle实例,会将入参直接返回。
MethodHandle constant(Class<?> type, Object value):返回一个没有入参的MethodHandle实例,返回固定的值value,value的类型就是type

测试用例如下:


import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.Assert.assertEquals;public class MethodHandlesTest {@Testpublic void arrayTest() throws Throwable {int[] a={1,2,3};//参考源码对应的数组读操作的方法名是getElementMethodHandle getter= MethodHandles.arrayElementGetter(a.getClass());//通过lookup方法返回的Lookup实例无法查找上述方法
//        MethodHandle getter= MethodHandles.lookup().findStatic(a.getClass(),"getElement", MethodType.methodType(a.getClass().getComponentType(), a.getClass(), int.class));int a_0=(int)getter.bindTo(a).invoke(0);int a_1=(int)getter.bindTo(a).invoke(1);int a_2=(int)getter.bindTo(a).invoke(2);System.out.println("a="+a_0+","+a_1+","+a_2);//参考源码对应的数组写操作的方法名是setElementMethodHandle setter= MethodHandles.arrayElementSetter(a.getClass());setter.invoke(a,0,11);setter.invoke(a,1,22);setter.invoke(a,2,33);System.out.println(Arrays.toString(a));//Java 反射a_0=Array.getInt(a,0);a_1=Array.getInt(a,1);a_2=Array.getInt(a,2);System.out.println("a="+a_0+","+a_1+","+a_2);Array.set(a,0,31);Array.set(a,1,32);Array.set(a,2,33);System.out.println(Arrays.toString(a));}@Testpublic void invokerTest() throws Throwable {MethodType methodType= methodType(String.class,String.class,int.class);MethodHandle target = publicLookup().findStatic(MethodHandlesTest.class, "invokee",
//                MethodType.genericMethodType(0, true));methodType(String.class,String.class,int.class));//将MethodHandle按照目标methodType做适配target = target.asType(methodType);MethodHandle inv=MethodHandles.exactInvoker(methodType);//返回的MethodHandle必须通过invokeWithArguments调用,否则各种类型转换报错String s=(String)inv.invokeWithArguments(target,"test",1);System.out.println(s);inv=MethodHandles.invoker(methodType);s=(String)inv.invokeWithArguments(target,"test",2);System.out.println(s);inv=MethodHandles.spreadInvoker(methodType,0);s=(String)inv.invokeWithArguments(target,new Object[]{"test",3});System.out.println(s);inv=MethodHandles.spreadInvoker(methodType,1);s=(String)inv.invokeWithArguments(target,"test",new Object[]{4});System.out.println(s);}//    public static String invokee(Object... args) {
//        return Arrays.toString(args);
//    }public static String invokee(String s,int a) {return "s="+s+",a="+a;}@Testpublic void ArgumentsTest() throws Throwable {MethodHandle target = publicLookup().findStatic(MethodHandlesTest.class, "invokee",methodType(String.class,String.class,int.class));//报错cannot convert MethodHandle(Object,Long)String to (String,int)String
//        MethodHandle newHandle=MethodHandles.explicitCastArguments(target,MethodType.methodType(String.class,Object.class,Long.class));//如果执行asType方法就可以转换成目标type则直接使用asType方法完成转换,否则尝试额外的转换MethodHandle newHandle=MethodHandles.explicitCastArguments(target, methodType(String.class,Object.class,long.class));String result=(String)newHandle.invoke("ss",2);System.out.println(result);}@Testpublic void ArgumentsTest2() throws Throwable {MethodType intfn1 = methodType(int.class, int.class);MethodType intfn2 = methodType(int.class, int.class, int.class);MethodHandle sub = lookup().findStatic(this.getClass(),"sub",intfn2);assert(sub.type().equals(intfn2));//通过reorder改变实际传递参数的顺序MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);assert((int)rsub.invokeExact(1, 100) == 99);assert((int)sub1.invokeExact(1, 100) == -99);MethodHandle add = lookup().findStatic(this.getClass(),"add",intfn2);assert(add.type().equals(intfn2));MethodHandle twice = permuteArguments(add, intfn1, 0, 0);assert(twice.type().equals(intfn1));assert((int)twice.invokeExact(21) == 42);}public static int sub(int x,int y){return x-y;}public static int add(int x,int y){return x+y;}public static int add(int x,int y,int z){return x+y+z;}@Testpublic void ArgumentsTest3() throws Throwable {MethodHandles.Lookup lookup= lookup();MethodType intfn4 = methodType(int.class, int.class, int.class,int.class);MethodType intfn3 = methodType(int.class, int.class, int.class);MethodType intfn2 = methodType(int.class, int.class);MethodHandle add=lookup.findStatic(this.getClass(),"add",intfn4);int result=(int)add.invoke(1,2,3);System.out.println(result);//将4作为参数索引为2的参数的绑定值,即该参数固定为4,前面索引1,2的参数由invoke方法传入//使用固定参数后,该参数对应的参数类型从原来的MethodType中去除MethodHandle addNew=MethodHandles.insertArguments(add,2,4);result=(int)addNew.invoke(1,2);System.out.println(result);assertEquals(addNew.type(),intfn3);addNew=MethodHandles.insertArguments(add,1,5,6);result=(int)addNew.invoke(1);System.out.println(result);assertEquals(addNew.type(),intfn2);}@Testpublic void ArgumentsTest4() throws Throwable {MethodHandle cat = lookup().findVirtual(String.class,"concat", methodType(String.class, String.class));assertEquals("xy", (String) cat.invokeExact("x", "y"));MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);System.out.println(bigType);List<Class<?>> classList=bigType.parameterList().subList(0,2);System.out.println(classList);//生成的MethodHandle会丢弃掉从参数索引0开始的classList的参数,使用剩余的参数调用原来cat绑定的方法
//        MethodHandle d0 = dropArguments(cat, 0, classList);MethodHandle d0 = dropArguments(cat, 0, int.class, String.class);//为了跟丢弃的参数适配,生成的MethodHandle的MethodType相当于在原来的MethodType的基础上增加classList对应的参数类型assertEquals(bigType, d0.type());assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));d0 = dropArguments(cat, 1, int.class, String.class);assertEquals("xz", (String) d0.invokeExact( "x",123, "y", "z"));}@Testpublic void ArgumentsTest5() throws Throwable {MethodHandle cat = lookup().findVirtual(String.class,"concat", methodType(String.class, String.class));MethodHandle upcase = lookup().findVirtual(String.class,"toUpperCase", methodType(String.class));assertEquals("xy", (String) cat.invokeExact("x", "y"));//filterArguments添加若干个参数的预处理MethodHandle,cat执行前会使用upcase从指定的参数开始预处理MethodHandle f0 = filterArguments(cat, 0, upcase);assertEquals("Xy", (String) f0.invokeExact("x", "y")); // XyMethodHandle f1 = filterArguments(cat, 1, upcase);assertEquals("xY", (String) f1.invokeExact("x", "y")); // xYMethodHandle f2 = filterArguments(cat, 0, upcase, upcase);assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY}@Testpublic void ArgumentsTest6() throws Throwable {MethodHandle deepToString = publicLookup().findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));MethodHandle ts1 = deepToString.asCollector(String[].class, 1);assertEquals("[strange]", (String) ts1.invokeExact("strange"));MethodHandle ts2 = deepToString.asCollector(String[].class, 2);assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));MethodHandle ts3 = deepToString.asCollector(String[].class, 3);//从参数索引1的位置开始的参数执行ts2,将执行结果作为参数执行ts3MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);assertEquals("[top, [up, down], strange]",(String) ts3_ts2.invokeExact("top", "up", "down", "strange"));MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);assertEquals("[top, [up, down], [strange]]",(String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));//从参数索引1的位置开始的参数执行ts3,将执行结果作为参数执行ts3_ts2MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);assertEquals("[top, [[up, down, strange], charm], bottom]",(String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));}@Testpublic void ArgumentsTest7() throws Throwable {MethodHandle upcase = lookup().findVirtual(String.class,"toUpperCase", methodType(String.class));MethodHandle cat = lookup().findVirtual(String.class,"concat", methodType(String.class, String.class));assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));//upcase也是预处理,不同的是预处理的结果会做一个新的参数插入到原来的参数列表的前面,然后执行catMethodHandle catTrace = foldArguments(cat, upcase);assertEquals("BOOboo", (String) catTrace.invokeExact("boo"));}@Testpublic void ArgumentsTest8() throws Throwable {MethodHandle cat = lookup().findVirtual(String.class,"concat", methodType(String.class, String.class));MethodHandle length = lookup().findVirtual(String.class,"length", methodType(int.class));System.out.println((String) cat.invokeExact("x", "y")); // xy//filterReturnValue是后处理,先执行cat,执行的结果作为参数执行length,返回最终的结果MethodHandle f0 = filterReturnValue(cat, length);System.out.println((int) f0.invokeExact("x", "y")); // 2}@Testpublic void specialMethodHandleTest() throws Throwable {//返回的MethodHandle接收特定类型的异常的实例,然后抛出异常MethodHandle methodHandle=MethodHandles.throwException(void.class,UnsupportedOperationException.class);methodHandle.invoke(new UnsupportedOperationException());}@Testpublic void specialMethodHandleTest2() throws Throwable {//返回的MethodHandle接收指定类型的唯一一个参数,并将其返回MethodHandle methodHandle=MethodHandles.identity(String.class);System.out.println((String)methodHandle.invoke("s"));}@Testpublic void specialMethodHandleTest3() throws Throwable {//返回的MethodHandle实例永远返回一个指定类型的固定的值,没有任何入参MethodHandle methodHandle=MethodHandles.constant(String.class,"test");System.out.println((String)methodHandle.invoke());System.out.println((String)methodHandle.invoke());}
}

三、与Java Reflect的区别

可以用Java Reflect实现第一个测试用例的效果么?答案是可以,如下:

import java.lang.reflect.Method;class TestA{public void println(String s){System.out.println("TestA println:"+s);}
}class TestB{public void println(String s){System.out.println("TestB println:"+s);}
}public class MethodHandlerTest {public static void reflectTest(Object obj,String s) throws Throwable{Method method=obj.getClass().getMethod("println",String.class);method.invoke(obj,s);}public static void main(String[] args) throws Throwable{String test="Hello World";reflectTest(new TestA(),test);reflectTest(new TestB(),test);reflectTest(System.out,test);}
}

上述示例和之前使用MethodHandle操作字段,普通方法,构造方法及数组的示例中,我们用Java反射的API执行了相同操作,反射的代码明显比MethodHandle更简洁明了,那MethodHandle的价值在哪?

MethodHandle最大的价值在于其更轻量,只包含方法调用必要的信息,是对方法调用句柄而非方法本身的抽象;反射中的Method类是Java方法在Java语言层面的一个完整抽象,包含方法的注解,参数类型,返回类型,修饰符等跟方法调用本身不直接相关的东西。

MethodHandle在执行时不会检查是否具有调用权限,只在MethodHandle创建时检查;而Method类执行的方法调用或者属性操作等是每次执行都会检查是否具有调用权限,因此MethodHandle可以省掉一次调用权限检查的损耗

MethodHandle的底层实现是模拟字节码的行为,而Java反射只是Java层面的层层方法调用,调用链更长,理论上MethodHandle的性能更好。

Hotspot MethodHandle详解相关推荐

  1. Hotspot JNIEnv API详解(二)

    目录 一.字符串操作 1.常见的编码格式 2.乱码问题根源 3.字符串API 4.jni_NewString和jni_NewStringUTF源码解析 二.数组操作 三.Monitor操作 四.NIO ...

  2. Hotspot JNIEnv API详解(一)

    目录 一.数据类型 1.Java数据类型 2.JNI数据类型 3.Java同JNI数据类型的对应关系 二.API定义 1.JNI标准API的发展 2.JNIEnv和JavaVM定义 三.异常处理 四. ...

  3. 中yeti不能加载_第二十章_类的加载过程详解

    类的加载过程详解 概述 在 Java 中数据类型分为基本数据类型和引用数据类型.基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载 按照 Java 虚拟机规范,从 Class 文件到加载到内 ...

  4. JVM详解(类加载,内存分配,GC,内存模型)

    一. 背景 1. 前言 作为一位Java开发工作者,在关心业务代码开发的同时,我们也需要了解java底层时如何运作的,了解为什么说java是跨平台的语言,所以这一篇对JVM(java虚拟机)进行剖析和 ...

  5. linux卸载hadoop版本,centos6.5 安装hadoop1.2.1的教程详解【亲测版】

    本篇只简单介绍安装步骤 1. 角色分配 10.11.84.4 web-crawler--1.novalocal master/slave 10.11.84.5 web-crawler--2.noval ...

  6. spring boot 实战 / 可执行war启动参数详解

    概述   上一篇文章<spring boot 实战 / mvn spring-boot:run 参数详解>主要讲解了spring boot 项目基于maven插件启动过程中借助profil ...

  7. 还在用JDK6的同学,来看看JDK13新特性详解吧

    点击上方"搜云库技术团队"关注,选择"设为星标" 回复"面试题"或"1024"获取 4T 学习资料 在 JDK 版本的世 ...

  8. 并发编程专题——第二章(并发编程之Synchronized详解)

    日常中我们都会用到Synchronized关键字,但是面试就喜欢问这些,你说不重要吧,面试就不问了,你说重要吧,工作中除了高并发之外,很少能在业务代码中使用到的.所以笔者顶着风险,写下此篇对Synch ...

  9. 【JAVA笔记——道】对象生命周期详解

    JAVA对象实例化过程 Class初始化理解 此篇中详细介绍了JAVA对象的实例化过程 JAVA对象内存分配过程 JVM 这里默认使用HotSpot虚拟机.简单回顾一下JVM内存结构,JVM中主要将使 ...

最新文章

  1. 分享:Dlib 17.49 发布,跨平台 C++ 通用库
  2. invalid character in identifier
  3. 刨根问底儿 -- intVal($str) 跟 (int) $str 的运算结果有什么区别
  4. 如何自学python数据分析-如何轻松学习Python数据分析?
  5. aws fargate_借助Fargate和EKS,AWS甚至可以实现Cloud-ier和Kuberneties-ier
  6. css 字体倾斜_css笔记3(文本,列表,背景声明)
  7. 95-864-040-源码-HDFS-Flink 和 HDFS 的交互
  8. 设计灵感|简单的几何图形还能这么用在网页设计中!
  9. java扫描一个端口状态_java扫描端口,如何判断端口是用来做什么的(提供什么服务的)?...
  10. linux route配置网关,Linux使用route配置路由
  11. 七号信令:信令网基本概念
  12. AI之DS:人工智能领域之数据科学领域六大实践场景(金融信用违约、反欺诈模型、客户偏好洞察、智能推荐、精准营销、客户流失管理)所对应的机器学习算法总结(持续更新)
  13. python如何通过以太网发送指令_Python编写的客户端给服务器发送指令执行相应的命令并返回结果...
  14. 抖音的服务器究竟有多大
  15. 每日一淘赋能产品经济全面发展
  16. 【文本挖掘】关键词提取
  17. 与现代教育技术有关的计算机知识,基于计算机的现代教育技术手段的利用与开发...
  18. C#使用WebProxy实现代理访问webservice
  19. window.open 服务器运行失败,win10 openssh服务器安装失败的最佳解决方法
  20. 判断给出的秒数是几天几小时几分几秒

热门文章

  1. 包子笔记 - 银行股投资超额收益太难
  2. python match函数_什么是python re.match函数?(实例解析)
  3. 微信遮罩php,微信小程序 遮罩功能实现
  4. 阿里云Redis 外网访问
  5. 洋桃开发板笔记(八 )ADC初识——模数转换
  6. 大数据的列式存储格式:Parquet
  7. 能力重构|能力重塑|能力再造
  8. java enclose class_Enclosing Class
  9. 双目立体视觉源代码 双目立体视觉匹配程序 双目视觉3d成像(三维重构图像处理) 基于双目视觉的深度计算和三维重建 opencv写的双目视觉摄像机标定和三维重建代码
  10. Notificaton+IntentService下载