java7增加了一个invokedynamic的指令,这个命令是为了更好地支持JVM平台上的动态语言和lambda表达式。在给出一个使用示例时,我们先看四个概念
方法句柄(MethodHandle
):这个很像一个方法的代理,通过它就可以调用一个方法。(class文件常量池中的CONSTANT_MethodHandle就是方法句柄)
调用点(CallSite
):这个是对方法句柄的一个封装,通过在变调用点上设置不同的方法句柄就可以调用不同的方法
启动方法(BootstrapMethods
):通过启动方法可以获得调用点
方法类型(MethodType
):主要用于设置方法的参数和返回值
下面我们看一下方法句柄的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.lang.invoke.MethodHandle;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodType;import java.time.LocalDateTime;public class TestMethodHandler { public static void main (String[] args) throws Throwable { MethodType methodType = MethodType.methodType(void .class); MethodHandle printHelloWorld = MethodHandles.lookup().findStatic(TestMethodHandler.class, "printHelloWorld" , methodType); printHelloWorld.invoke(); TestMethodHandler testMethodHandler = new TestMethodHandler(); MethodHandle printTime = MethodHandles.lookup().findVirtual(TestMethodHandler.class, "printTime" , methodType).bindTo(testMethodHandler); printTime.invoke(); } public static void printHelloWorld () { System.out.println("Hello World!" ); } public void printTime () { System.out.println(LocalDateTime.now().toString()); } }
结果为
1 2 Hello World! 2016 -07 -24T01:05 :31.010
下面我们依次看一下上面的例子中使用的方法
findStatic()
: 查找一个static方法,然后使用invokestatic进行函数调用
findVirtual()
: 查找一个虚方法,使用invokevirtual指令进行函数调用
findSpecial()
: 查找私有方法或者父类的方法,使用invokespecial指令进行调用
findConstructor()
:查找构造器方法
下面我们接着看一下调用点,调用点一共分为3种
我们接着看一下常量调用点的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.lang.invoke.*;import java.time.LocalDateTime;public class TestCallSite { public static void main (String[] args) throws Throwable { MethodType methodType = MethodType.methodType(void .class); MethodHandle printHelloWorld = MethodHandles.lookup().findStatic(TestCallSite.class, "printHelloWorld" , methodType); CallSite callSite = new ConstantCallSite(printHelloWorld); MethodHandle invoker = callSite.dynamicInvoker(); invoker.invoke(); MethodHandle printTime = MethodHandles.lookup().findStatic(TestCallSite.class, "printTime" , methodType); callSite.setTarget(printTime); invoker.invoke(); } public static void printHelloWorld () { System.out.println("Hello World!" ); } public static void printTime () { System.out.println(LocalDateTime.now().toString()); } }
结果为
1 2 3 4 5 6 7 8 9 Exception in thread "main" java.lang.UnsupportedOperationException at java.lang.invoke.ConstantCallSite.setTarget(ConstantCallSite.java:106 ) at TestCallSite.main(TestCallSite.java:18 ) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62 ) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 ) at java.lang.reflect.Method.invoke(Method.java:498 ) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147 ) Hello World!
首先我们发现,当再一个常量调用点上进行重置target的时候,产生了一个异常,那么我们使用可变调用点试试呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.lang.invoke.*;import java.time.LocalDateTime;public class TestCallSite { public static void main (String[] args) throws Throwable { MethodType methodType = MethodType.methodType(void .class); MethodHandle printHelloWorld = MethodHandles.lookup().findStatic(TestCallSite.class, "printHelloWorld" , methodType); CallSite callSite = new MutableCallSite(printHelloWorld); MethodHandle invoker = callSite.dynamicInvoker(); invoker.invoke(); MethodHandle printTime = MethodHandles.lookup().findStatic(TestCallSite.class, "printTime" , methodType); callSite.setTarget(printTime); invoker.invoke(); } public static void printHelloWorld () { System.out.println("Hello World!" ); } public static void printTime () { System.out.println(LocalDateTime.now().toString()); } }
结果为
1 2 Hello World! 2016 -07 -24T01:18 :02.905