invoke方法用于动态调用一个方法,为Method类下的一个public方法,函数签名public Object invoke(Object root, Object...parameters) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
invoke方法是实现代理的重要方法,先从一个简单的例子开始。
package com.godder.test;
import java.lang.reflect.Method;
public class AClass {
public void hello(String name) {
System.out.println("hello " + name);
}
public static void main(String[] args) throws Throwable {
Method method = AClass.class.getMethod("hello", String.class);
method.invoke(AClass.class.newInstance( "godder");
}
}
输出结果为: hello godder
通过javap指令获取main方法的字节码如下:
public static void main(java.lang.String[]) throws java.lang.Throwable;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Exceptions:
throws java.lang.Throwable
Code:
stack=6, locals=2, args_size=1
0: ldc #1 // class com/godder/test/AClass
2: ldc #48 // String hello
4: iconst_1
5: anewarray #49 // class java/lang/Class
8: dup
9: iconst_0
10: ldc #51 // class java/lang/String
12: aastore
13: invokevirtual #53 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
16: astore_1
17: aload_1
18: ldc #1 // class com/godder/test/AClass
20: invokevirtual #57 // Method java/lang/Class.newInstance:()Ljava/lang/Object;
23: iconst_1
24: anewarray #3 // class java/lang/Object
27: dup
28: iconst_0
29: ldc #61 // String godder
31: aastore
32: invokevirtual #63 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
35: pop
36: return
其中invokevirtual指令出现了3次,分别是调用方法:
Class.getMethod(); Class.newInstance(); Method.invoke();
这三个方法都是虚方法(除static方法,private方法,constructor方法,super方法,final方法外的方法)
现在用另外一个例子
package com.godder.test;
import java.lang.reflect.Method;
public class BClass extends AClass {
public static void fuker(String name) {
System.out.println("fuker " + name);
}
public static void main(String[] args) throws Throwable {
BClass clz = new BClass();
Method method1 = clz.getClass().getMethod("hello", String.class);
method1.invoke(clz, "godder");
Method method2 = clz.getClass().getMethod("fuker", String.class);
method2.invoke(null, "godder");
BClass.fuker("god`
er");
}
}
``
输出结果为:
hello godder
fuker godder
fuker godder
由javap指令获取的main函数的字节码如下:
public static void main(java.lang.String[]) throws java.lang.Throwable;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Exceptions:
throws java.lang.Throwable
Code:
stack=6, locals=4, args_size=1
0: new #1 // class com/godder/test/BClass
3: dup
4: invokespecial #48 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #49 // Method java/lang/Object.getClass:()Ljava/lang/Class;
12: ldc #55 // String hello
14: iconst_1
15: anewarray #57 // class java/lang/Class
18: dup
19: iconst_0
20: ldc #59 // class java/lang/String
22: aastore
23: invokevirtual #61 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
26: astore_2
27: aload_2
28: aload_1
29: iconst_1
30: anewarray #50 // class java/lang/Object
33: dup
34: iconst_0
35: ldc #65 // String godder
37: aastore
38: invokevirtual #67 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
41: pop
42: aload_1
43: invokevirtual #49 // Method java/lang/Object.getClass:()Ljava/lang/Class;
46: ldc #73 // String fuker
48: iconst_1
49: anewarray #57 // class java/lang/Class
52: dup
53: iconst_0
54: ldc #59 // class java/lang/String
56: aastore
57: invokevirtual #61 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
60: astore_3
61: aload_3
62: aconst_null
63: iconst_1
64: anewarray #50 // class java/lang/Object
67: dup
68: iconst_0
69: ldc #65 // String godder
71: aastore
72: invokevirtual #67 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
75: pop
76: ldc #65 // String godder
78: invokestatic #74 // Method fuker:(Ljava/lang/String;)V
81: return
这里出现了invokespecial指令用于调用构造器<init>方法来创建一个BClass的实例;
同时这里通过俩种不同的方法来调用static方法:fuker(String name),但这里却出现了不同的字节码。通过invoke()方法调用的字节码为invokevirtual,直接调用该方法的字节码为invokestatic。这里说明了invoke方法实现了动态的调用。由于java是静态编译语言,与python,js等动态编译语言不同,java在编译之前必须确定变量的类型,并且严格按照类型约束,如果类型不匹配则编译失败。
现在再来一个invokeinterface的例子:
package com.godder.test;
import java.lang.reflect.Method;
public class CClass implements Interface {
@Override
public void silyB(String name) {
System.out.println(name + " is a SB by C");
}
public static void main(String[] args) throws Throwable{
Interface clz = new CClass();
CClass clz2 = new CClass();
Method method = clz.getClass().getMethod("silyB", String.class);
method.invoke(clz, "godder");
clz.silyB("godder");
clz2.silyB("go`
der”);
}
}
“`
输出结果为:
godder is a SB by C
godder is a SB by C
godder is a SB by C
由javap指令得出的main方法的字节码为:
public static void main(java.lang.String[]) throws java.lang.Throwable;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Exceptions:
throws java.lang.Throwable
Code:
stack=6, locals=4, args_size=1
0: new #1 // class com/godder/test/CClass
3: dup
4: invokespecial #56 // Method “”:()V
7: astore_1
8: new #1 // class com/godder/test/CClass
11: dup
12: invokespecial #56 // Method “”:()V
15: astore_2
16: aload_1
17: invokevirtual #57 // Method java/lang/Object.getClass:()Ljava/lang/Class;
20: ldc #61 // String silyB
22: iconst_1
23: anewarray #62 // class java/lang/Class
26: dup
27: iconst_0
28: ldc #27 // class java/lang/String
30: aastore
31: invokevirtual #64 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
34: astore_3
35: aload_3
36: aload_1
37: iconst_1
38: anewarray #3 // class java/lang/Object
41: dup
42: iconst_0
43: ldc #68 // String godder
45: aastore
46: invokevirtual #70 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
49: pop
50: aload_1
51: ldc #68 // String godder
53: invokeinterface #76, 2 // InterfaceMethod com/godder/test/Interface.silyB:(Ljava/lang/String;)V
58: aload_2
59: ldc #68 // String godder
61: invokevirtual #78 // Method silyB:(Ljava/lang/String;)V
64: return
可以看出,类型为Interface的对象调用的方法对应的字节码为invokeinterface,由具体类为类型的对象调用的方法对应的字节码为invokevirtual。
在jvm中还有一个invokedynamic指令,用于支持动态类型语言。但鉴于其内容,我希望将他放在一篇单独的博客里,以上便是关于invoke方法和invoke字节码的基础部分类容,如果要讨论invoke方法的具体实将涉及native方法和jvm底层,故不在此深究。