從某種程度上,invokedynamic與MethodHandle機(jī)制的作用是一樣的,都是為了解決原來的4條指令"invoke*"指令方法將分派規(guī)則固化在虛擬機(jī)中的問題,如何將查找方法的決定權(quán)從虛擬機(jī)轉(zhuǎn)移到具體的用戶代碼中??蓪⑺鼈兿胂蟪梢粋€(gè)使用上層的java API實(shí)現(xiàn),另一個(gè)使用字節(jié)碼中和class中的其它屬性,常量來完成。
含有invokedynamic指令的位置被稱為動(dòng)態(tài)調(diào)用點(diǎn)(Dynamic Call Site),這個(gè)指令的第一個(gè)參數(shù)不再是代表方法符號(hào)引用 的CONSTANT_Methodref_info常量,而是jdk1.7中的CONSTANT_InvokeDynamic_info常量,里面有3個(gè)信息:
1.引導(dǎo)方法(Bootstrap Method),有固定的參數(shù),且返回值是java.lang.invoke.CallSite對(duì)象,代表真正要執(zhí)行的方法調(diào)用。
2.方法類型(MethodType)
3.方法名稱
這樣我們就可以根據(jù)CONSTANT_InvokeDynamic_info常量中的信息找到并執(zhí)行引導(dǎo)方法,并得到一個(gè)CallSite對(duì)象,最終調(diào)用要執(zhí)行的方法。
package vmrun;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class InvokeDynamicTest {
public static void main(String [] args) throws Throwable{
INDY_BootstrapMethod().invokeExact("lsj");
}
public static void testMethod(String s ){
System.out.println("hello:"+ s);
}
public static CallSite BootstrapMethod(MethodHandles.Lookup lookup, String name ,MethodType type) throws Throwable{
System.out.println("BootstrapMethod");
return new ConstantCallSite(lookup.findStatic(InvokeDynamicTest.class, name, type)) ;
}
private static MethodType MT_BootstrapMethod(){
System.out.println("MT_BootstrapMethod");
return MethodType.fromMethodDescriptorString(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
null);
}
private static MethodHandle MH_BootsrapMethod()throws Throwable{
System.out.println("MH_BootsrapMethod");
return MethodHandles.lookup().findStatic(InvokeDynamicTest.class,"BootstrapMethod", MT_BootstrapMethod());
}
private static MethodHandle INDY_BootstrapMethod() throws Throwable{
System.out.println("INDY_BootstrapMethod");
CallSite sCallSite = (CallSite)MH_BootsrapMethod().invokeWithArguments(MethodHandles.lookup(), "testMethod",
MethodType.fromMethodDescriptorString("(Ljava/lang/String;)V", null));
return sCallSite.dynamicInvoker();
}
}
輸出為
INDY_BootstrapMethod
MH_BootsrapMethod
MT_BootstrapMethod
BootstrapMethod
hello:lsj
invokedynamic方法與前面的invoke方法最大的不同在于它的分派邏輯不是由虛擬機(jī)決定的,而是由程序員決定的。*
如下面的問題
public class Test {
class GrandFather {
void thinking (){
System.out.println("grandfather");
}
}
class Father extends GrandFather {
void thinking (){
System.out.println("father");
}
}
class Son extends Father{
void thinking (){
/**
* 如何 實(shí)現(xiàn) 調(diào)用 grandfather的方法
*/
}
}
}
在jdk1.7前,可以用super實(shí)現(xiàn)調(diào)用father的方法,但是無法實(shí)現(xiàn)調(diào)用 grandfaher的方法,原因是在son中無法得到一個(gè)實(shí)際類型是grandfather的對(duì)象引用(overriding),而invokevirtual的分派邏輯就是按照方法的實(shí)際接收者的實(shí)際類型來進(jìn)行分派 ,這是固化在虛擬機(jī)中的,無法在代碼中改變。在jdk1.7中,我們可以在代碼中解決這個(gè)問題。
package vmrun;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class Test {
class GrandFather {
void thinking (){
System.out.println("grandfather");
}
}
class Father extends GrandFather {
void thinking (){
System.out.println("father");
}
}
class Son extends Father{
void thinking (){
System.out.println("son");
/**
* 如何 實(shí)現(xiàn) 調(diào)用 grandfather的方法
*/
try {
MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.lookup().findSpecial(GrandFather.class,
"thinking", mt, getClass()) ;
mh.invoke(this) ;
} catch (Throwable e) {
e.printStackTrace();
}
}
}
public static void main(String [] args){
(new Test().new Son()).thinking();
}
}
輸出為:
son
father
5種invoke方法:
- invokestatic調(diào)用靜態(tài)方法。
- invokespecial:調(diào)用 實(shí)例構(gòu)造器<init>,私有方法,父類方法。
- invokevirtual:調(diào)用所有的虛方法,final方法也由其調(diào)用 ,但不是虛方法。
- invokeinterface:調(diào)用接口方法,會(huì)在運(yùn)行時(shí)再確定一個(gè)實(shí)現(xiàn)此接口的類。
- invokedynamic:在運(yùn)行時(shí)動(dòng)態(tài)解析出CallSite所調(diào)用的方法,然后再執(zhí)行。