手机网站设计小程序,张家港网站建设优化,域名历史记录查询网站,目前网站在初级建设阶段 需要大量数据丰富在Java中#xff0c;方法调用会被编译为invokeStatic#xff0c;invokeSpecial#xff0c;invokVirtual以及invokeInterface四种指令。这些指令与包含目标方法类名、方法名以及方法描述符的符号引用捆绑#xff0c;在实际运行之前#xff0c;JVM根据这个符号引用链接到具体…在Java中方法调用会被编译为invokeStaticinvokeSpecialinvokVirtual以及invokeInterface四种指令。这些指令与包含目标方法类名、方法名以及方法描述符的符号引用捆绑在实际运行之前JVM根据这个符号引用链接到具体目标方法。
JDK7 引入新的指令invodeDynamic该指令的调用机制抽象出调用点这一个概念并允许应用程序将调用点链接至任意符合条件的方法上。同时JDK7 还配套引入了更加低层、更加抽象的方法抽象方法句柄invokedynamic 底层机制的基石方法句柄。。
一、方法句柄
1.方法句柄概念
强类型能够被直接执行的引用。 方法句柄的类型是由所指向方法的参数以及返回类型组成的。它是用来确认方法句柄是否适配的唯一关键。
方法句柄的获取
class Foo {private static void bar(Object o) {..}public static Lookup lookup() {return MethodHandles.lookup();}
}// 获取方法句柄的不同方式
MethodHandles.Lookup l Foo.lookup(); // 具备 Foo 类的访问权限
Method m Foo.class.getDeclaredMethod(bar, Object.class);
MethodHandle mh0 l.unreflect(m);MethodType t MethodType.methodType(void.class, Object.class);
MethodHandle mh1 l.findStatic(Foo.class, bar, t);
方法句柄的权限 与反射 API 不同其权限检查是在句柄的创建阶段完成的。在实际调用过程中JVM不会检查方法句柄的权限。 方法句柄的访问权限不取决于方法句柄的创建位置而是取决于 Lookup对象的创建位置 举个例子对于一个私有字段如果 Lookup 对象是在私有字段所在类中获取的则这个Lookup对象便拥有对该私有字段的访问权限
即使是在所在类的外边也能够通过该 Lookup 对象创建该私有字段的getter 或 setter2.方法句柄的操作
方法句柄的调用有两种模式
invokeExact需要严苛匹配参数类型 一个方法句柄将接收一个 Object 类型的参数如果你直接传入String作为实际参数则方法句柄的调用会在运行时抛出方法类型不匹配的异常invoke自动适配参数类型 invoke 会调用 MethodHandle.asType方法生成一个适配器方法句柄对传入的参数进行适配再调用原方法句柄调用原方法句柄的返回值同样会先进行适配然后再返回给调用者。
方法句柄支持增删改参数的操作
改操作MethodHandle.asType 方法删操作将传入的部分参数就地抛弃再调用另一个方法句柄。它对应的API 是 MethodHandles.dropArguments方法增操作它会往传入的参数中插入额外的参数再调用另一个方法句柄它对应的 API 是 MethodHandle.bindTo 方法Java 8 中捕获类型的 Lambda 表达式便是用这种操作来实现的
增操作还可以用来实现方法的柯里化。举个例子有一个指向 f(x, y) 的方法句柄我们可以通过将 x 绑定为 4生成另一个方法句柄 g(y) f(4, y)。
在执行过程中每当调用 g(y) 的方法句柄它会在参数列表最前面插入一个 4再调用指向 f(x, y) 的方法句柄。柯里化Currying是把接受多个参数的函数变换成接受一个单一参数最初函数的第一个参数的函数并且返回接受余下的参数而且返回结果的新函数的技术 3.方法句柄的实现
方法句柄的调用和反射调用一样都是间接调用。因此它也会面临无法内联的问题。不过与反射调用不同的是方法句柄的内联瓶颈在于即时编译器能否将该方法句柄识别为常量。
二、invokeDynamic指令
1.调用点介绍 invokedynamic 是 Java 7 引入的一条新指令用以支持动态语言的方法调用。具体来说它将调用点CallSite抽象成一个 Java 类并且将原本由 Java 虚拟机控制的方法调用以及方法链接暴露给了应用程序。在运行过程中每一条 invokedynamic 指令将捆绑一个调用点并且会调用该调用点所链接的方法句柄。 在第一次执行 invokedynamic 指令时Java 虚拟机会调用该指令所对应的启动方法BootStrap Method来生成前面提到的调用点并且将之绑定至该 invokedynamic 指令中。在之后的运行过程中Java 虚拟机则会直接调用绑定的调用点所链接的方法句柄。 invokedynamic 的目的就是将调用点与目标方法的链接交由应用程序来做并且依赖于应用程序对目标方法进行验证。所以如果应用程序将赛跑方法链接至兔子的睡觉方法那也只能怪应用程序自己了。
2.Java8 中lambda表达式
Java8中的lambda是借助于invokeDynamic来实现的。
具体来说Java 编译器利用 invokedynamic 指令来生成实现了函数式接口的适配器。 函数式接口指的是仅包括一个非 default 接口方法的接口一般通过 FunctionalInterface 注解,不过就算是没有使用该注解Java 编译器也会将符合条件的接口辨认为函数式接口 int x ..
IntStream.of(1, 2, 3).map(i - i * 2).map(i - i * x);上面这段代码会对 IntStream 中的元素进行两次映射。映射方法 map 所接收的参数是 IntUnaryOperator这是一个函数式接口。
也就是说在运行过程中我们需要将 i-i*2 和 i - i*x 这两个lambda表达式转化成IntUnaryOperator实例
这个转换过程就是通过invokeDynamic实现的在编译过程中Java 编译器会对 Lambda 表达式进行解语法糖desugar
生成一个方法来保存 Lambda 表达式的内容。该方法的参数列表不仅包含原本 Lambda 表达式的参数还包含它所捕获的变量。
(注方法引用如 Horse::race则不会生成生成额外的方法。)在上面那个例子中第一个 Lambda 表达式没有捕获其他变量而第二个 Lambda 表达式也就是 i-i*x则会捕获局部变量 x。
这两个 Lambda 表达式对应的方法如下所示。可以看到所捕获的变量同样也会作为参数传入生成的方法之中。