《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》8.3.3节
动态类型语言关键特征是,类型检查主体过程是在运行期而不是编译期
JDK1.7实现JSR-292,新加入java.lang.invoke包,该包主要目的是在依靠符号引用确定目标方法以外,提供一种新的动态确定目标方法的机制,称
MethodHandle
拥有MethodHandle后,Java语言拥有类似函数指针或委托的方法别名的工具
在Java语言角度,MethodHandle使用方法和效果与Reflection有相似之处,但它们有以下区别:
1)
从本质上讲,Reflection和MethodHandle机制都在模拟方法调用,但Reflection是在
模拟J
ava代码层次的方法调用,而MethodHandle是在
模拟字节码层次的方法调用
MethodHandles.lookup中的3个方法,findStatic()、findVirtual()、findSpecial(),对应invokestatic、invokevirtual&invokeinterface和invokespecial指令的执行权限校验行为,这些底层细节在使用ReflectionAPI时不需关心
2)
Reflection中的java.lang.reflect.Method对象远比MethodHandle中的java.lang.invoke.MethodHandle对象包含的信息多
前者是方法在Java端的全面映像,包含方法的签名、描述符及方法属性表中各种属性的Java端表示方式,还包含执行权限等运行期信息
后者仅仅包含与执行该方法相关的信息
通俗的讲,Reflection是
重量级,而MethodHandle是
轻量级
3)
MethodHandle是对字节码方法指令调用的模拟,故理论上虚拟机在这方面做的各种
优化(如方法内联),在MethodHandle上也应当可以采用类似思路去支持(但目前实现还不完善)
而通过反射去调用方法则不行
4)
MethodHandle与Reflection除上面列举的区别外,最关键的一点还在于去掉前面讨论施加的前提“仅站在Java语言的角度来看”:ReflectionAPI的设计目标是
只为Java语言服务,而MethodHandle则设计成可
服务所有Java虚拟机上的语言,其中包括Java语言
含有
invokedynamic指令的位置称“动态调用点”(Dynamic Call Site)
该指令的第一个参数不再是代表方法符号引用的CONSTANT_Methodref_info常量,而是JDK1.7新加入的CONSTANT_InvokeDynamic_info常量
从该新常量可得到3项信息:引导方法(BootstrapMethod,此方法存放在新增的BootstrapMethods属性中)、方法类型(MethodType)和名称
invokedynamic指令与其他4条"invoke*"指令的最大差别是,它的分派逻辑不由虚拟机决定,而由程序员决定
/**
* 使用MethodHandle解决问题
*/
public class TestMethodHandle {
class GrandFather {
void thinking() {
System.out.println(" i am grandfather");
}
}
class Father extends GrandFather {
void thinking() {
System.out.println(" i am father");
}
}
class Son extends Father {
void thinking() {
try {
MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = lookup().findSpecial(GrandFather.class, " thinking", mt, getClass());
mh.invoke(this);
} catch (Throwable e) {
}
}
}
public static void main(String[] args) {
(new Test().new Son()).thinking();
}
}