Step By Step(Java 反射篇)
这一篇并不想过多的介绍Java中关于反射的概念,我想即便是对于Java的初学者而言也并不感到陌生,该篇将一如既往的为您提供大量的可执行示例代码和丰富的注释信息,尽可能覆盖Java反射的各个技术环节,从更多的视角为您剖析反射的机制和应用场景,以及它在效率方面的不尽如人意之处。
1. 类属性反射:
每一个Java对象都会包含一个描述其属性(metadata)信息的类对象,该对象将作为Java反射中最重要的一个入口点,几乎所有和反射相关的应用都是从这里开始的。
1) 反射类的名字:
1 public static void main(String[] args) {
2 String str = "Hello";
3 //1. 获取Java对象的类对象(Class),也可以通过class域字段直接获取,如:
4 //c = str.class;
5 Class<? extends String> c = str.getClass();
6 //2. 获取Java对象的字符串表示的名称
7 System.out.println(c.getName());
8 }
9 /* 输出结果如下:
10 java.lang.String
11 */
2) 通过表示类名称的字符串获取该类的对象实例:
1 public static void main(String[] args) {
2 try {
3 //1. 通过表示类名的字符串,获取该类的类对象(描述String的类metadata信息)
4 Class<?> cls = Class.forName("java.lang.String");
5 //2. 再通过Class<String>对象的newInstance方法来构造
6 //String的实例,这样要求被实例化的类带有缺省构造函数。
7 //因为newInstance方法只有这一种签名方式。
8 String s = (String) cls.newInstance();
9 } catch (ClassNotFoundException e) {
10 e.printStackTrace();
11 } catch (InstantiationException e) {
12 e.printStackTrace();
13 } catch (IllegalAccessException e) {
14 e.printStackTrace();
15 }
16 }
3) 反射类的修饰符:
1 public static void main(String[] args) {
2 String str = "Hello";
3 Class<? extends String> c = str.getClass();
4 //获取的类在声明时定义的修饰符,既该类是否为public、final和abstract
5 int m = c.getModifiers();
6 if (Modifier.isPublic(m))
7 System.out.println("public");
8 if (Modifier.isAbstract(m))
9 System.out.println("abstract");
10 if (Modifier.isFinal(m))
11 System.out.println("final");
12 }
13 /* 输出结果如下:
14 public
15 final
16 */
4) 获取类的域字段、构造函数和域方法的访问修饰符信息:
1 public class MyTest {
2 public static void main(String[] args) {
3 MyTest pp = new MyTest();
4 pp.doClass("java.lang.String");
5 }
6 protected void doClass(String className) {
7 try {
8 Class<?> c = Class.forName(className);
9 System.out.println(Modifier.toString(c.getModifiers()) + ' ' + c + " {");
10 //通过类的metadata属性信息,获取类的域字段,但是不包括超类中的字段
11 //这其中getFields()方法将仅仅返回当前类的public域字段
12 Field fields[] = c.getDeclaredFields();
13 for (Field f : fields) {
14 if (Modifier.isPrivate(f.getModifiers())) {
15 System.out.println("Field '" + f.getName() + "' is private.");
16 } else if (Modifier.isProtected(f.getModifiers())) {
17 System.out.println("Field '" + f.getName() + "' is protected.");
18 } else if (Modifier.isPublic(f.getModifiers())) {
19 System.out.println("Field '" + f.getName() + "' is public.");
20 } else if (Modifier.isFinal(f.getModifiers())) {
21 System.out.println("Field '" + f.getName() + "' is final.");
22 } else if (Modifier.isStatic(f.getModifiers())) {
23 System.out.println("Field '" + f.getName() + "' is static.");
24 }
25 }
26 //获取类的构造函数,getConstructors仅仅返回该类的public构造函数。
27 //如果希望获得全部的构造函数,调用getDeclaredConstructors
28 Constructor<?>[] constructors = c.getConstructors();
29 for (Constructor<?> ctor : constructors) {
30 if (Modifier.isProtected(ctor.getModifiers())) {
31 System.out.println("Constructor '" + ctor.getName() + "' is protected.");
32 } else if (Modifier.isPrivate(ctor.getModifiers())) {
33 System.out.println("Constructor '" + ctor.getName() + "' is private.");
34 } else if (Modifier.isPublic(ctor.getModifiers())) {
35 System.out.println("Constructor '" + ctor.getName() + "' is public.");
36 }
37 }
38 //获取类中声明的域方法,但是不包含超类中的方法。
39 //这其中getMethods()方法将仅仅返回当前类的public域方法
40 Method methods[] = c.getDeclaredMethods();
41 for (Method m : methods) {
42 if (Modifier.isProtected(m.getModifiers())) {
43 System.out.println("Method '" + m.getName() + "' is protected.");
44 } else if (Modifier.isPrivate(m.getModifiers())) {
45 System.out.println("Method '" + m.getName() + "' is private.");
46 } else if (Modifier.isPublic(m.getModifiers())) {
47 System.out.println("Method '" + m.getName() + "' is public.");
48 } else if (Modifier.isStatic(m.getModifiers())) {
49 System.out.println("Method '" + m.getName() + "' is static.");
50 } else if (Modifier.isNative(m.getModifiers())) {
51 System.out.println("Method '" + m.getName() + "' is native.");
52 } else if (Modifier.isAbstract(m.getModifiers())) {
53 System.out.println("Method '" + m.getName() + "' is abstract.");
54 } else if (Modifier.isFinal(m.getModifiers())) {
55 System.out.println("Method '" + m.getName() + "' is final.");
56 }
57 }
58 System.out.println("}");
59 } catch (ClassNotFoundException e) {
60 System.err.println("Error: Class " + className + " not found!");
61 } catch (Exception e) {
62 System.err.println(e);
63 }
64 }
65 }
5) 获取超类的名称:
1 public static void main(String[] args) {
2 Class<?> subclass = LinkedList.class;
3 //获取超类的Class对象。
4 Class<?> superclass = subclass.getSuperclass();
5 while (superclass != null) {
6 String className = superclass.getName();
7 System.out.println(className);
8 subclass = superclass;
9 superclass = subclass.getSuperclass();
10 }
11 }
12 /* 输出结果如下:
13 java.util.AbstractSequentialList
14 java.util.AbstractList
15 java.util.AbstractCollection
16 java.lang.Object
17 */
6) 获取实现的接口:
在Java中并没有像C++那样支持多重继承,而是提供了一种更为安全和合理的方式,即单一继承和多实现,这里的单一继承是指继承(extends)一个超类,多实现是指可以实现(implements)多个接口。下面的例子将让我们获取某个类所实现的接口,如下:
1 public static void main(String[] args) {
2 Class<?>[] interfaces = LinkedList.class.getInterfaces();
3 if ((interfaces != null) && (interfaces.length > 0)) {
4 if (LinkedList.class.isInterface())
5 System.out.print(" extends ");
6 else
7 System.out.print(" implements ");
8 for (int i = 0; i < interfaces.length; i++) {
9 if (i > 0)
10 System.out.print(", ");
11 System.out.print(interfaces[i].getName());
12 }
13 }
14 }
15 /* 输出结果如下:
16 implements java.util.List, java.util.Deque, java.lang.Cloneable, java.io.Serializable
17 */
7) 获取包名:
1 public static void main(String[] args) {
2 System.out.println(String.class.getPackage().getName());
3 }
4 /* 输出结果如下:
5 java.lang
6 */
2. 基于数组的反射:
1) 通过反射创建数组实例:
1 public static void main(String[] args) {
2 int[] dim1 = { 5 };
3 //通过Array的newInstance方法构造新的数组实例,这里的dim1定义了
4 //oneDimA的维度,以及在该维度内的长度
5 int[] oneDimA = (int[]) Array.newInstance(int.class, dim1);
6 //第二个参数定义了一维数组的长度
7 int[] oneDimB = (int[]) Array.newInstance(int.class, 5);
8 System.out.println("The length of oneDimA is " + oneDimA.length);
9 System.out.println("The length of oneDimB is " + oneDimB.length);
10 if (Arrays.equals(oneDimA, oneDimB))
11 System.out.println("OneDimA is equal to oneDimB");
12
13 int[] dimStr = { 5, 10 };
14 //这里通过dimStr来定义一个5*10的二维String数组。
15 String[][] twoDimStr = (String[][]) Array.newInstance(String.class,dimStr);
16 System.out.println("The length of twoDimStr is " + twoDimStr.length);
17 for (int i = 0; i < twoDimStr.length; ++i) {
18 System.out.print(twoDimStr[i].length + "\t");
19 }
20 }
21 /* 输出结果如下:
22 The length of oneDimA is 5
23 The length of oneDimB is 5
24 OneDimA is equal to oneDimB
25 The length of twoDimStr is 5
26 10 10 10 10 10
27 */
2) 获取原始类型和原始类型数组的类型名称:
1 public static void main(String[] args) {
2 System.out.println(int.class.getName());
3 System.out.println(int[].class.getName());
4 System.out.println(byte.class.getName());
5 System.out.println(byte[].class.getName());
6 }
7 /* 输出结果如下:
8 int
9 [I
10 byte
11 [B
12 */
可以看到数组的名称前面带有一个[,这是数组名称的表示方式,二维数组则表示为[[,后面的I表示int,B表示byte。这是JVM提供的一套类型名称编码规范,我会在后面关于本地代码(JNI)的Blog中给出更为明确的列表和使用方式,因为他们在JNI中应用还是比较广泛的。
3) 判断当前对象是否为数组:
1 public static void main(String[] args) {
2 Object o = new int[10];
3 boolean b = o.getClass().isArray();
4 if (b) {
5 System.out.println("object is an array");
6 }
7 }
8 /* 输出结果如下:
9 object is an array
10 */
4) 获取数组的维度:
1 public class MyTest {
2 public static void main(String[] args) {
3 Object o = new int[1][2][3];
4 System.out.println("The length is " + Array.getLength(o));
5 System.out.println("The Dimension is " + getDim(o));
6 }
7 public static int getDim(Object array) {
8 int dim = 0;
9 Class<?> cls = array.getClass();
10 while (cls.isArray()) {
11 ++dim;
12 //getComponentType获取数组元素的Class对象,
13 //如果不是数组返回null。
14 cls = cls.getComponentType();
15 }
16 return dim;
17 }
18 }
19 /* 输出结果如下:
20 The length is 1
21 The Dimension is 3
22 */
5) 通过反射填充和显示数组元素:
1 public class MyTest {
2 public static void main(String args[]) {
3 Object array = Array.newInstance(int.class, 3);
4 fillArray(array);
5 displayArray(array);
6 }
7 private static void fillArray(Object array) {
8 //这里是通过反射的方式获取数组的长度,效率会低于
9 //通过数组对象的length方法获取,因此这里的例子缓存
10 //了该长度,而不是直接放到for循环的第二个表达式
11 int length = Array.getLength(array);
12 for (int i = 0; i < length; i++) {
13 //设置array数组的第i个元素的值为i*i。
14 Array.setInt(array, i, i*i);
15 }
16 }
17 private static void displayArray(Object array) {
18 int length = Array.getLength(array);
19 for (int i = 0; i < length; i++) {
20 //获取array数组的第i个元素,并返回int值。
21 int value = Array.getInt(array, i);
22 System.out.println("Position: " + i + ", value: " + value);
23 }
24 }
25 }
26 /* 输出结果如下:
27 Position: 0, value: 0
28 Position: 1, value: 1
29 Position: 2, value: 4
30 */
6) 基于数组对象再通过反射的机制创建一个相同的数组对象:
1 public class MyTest {
2 public static void main(String args[]) {
3 int[] ints = new int[2];
4 Object ret = buildNewArrayWithReflection(ints);
5 if (ret != null) {
6 Arrays.equals(ints, (int[])ret);
7 System.out.println("The both array are equal.");
8 }
9 }
10 private static Object buildNewArrayWithReflection(Object source) {
11 if (!source.getClass().isArray()) {
12 System.out.println("The argument is NOT an array.");
13 return null;
14 }
15 Class<?> arrayClass = source.getClass();
16 String arrayName = arrayClass.getName();
17 Class<?> componentClass = arrayClass.getComponentType();
18 String componentName = componentClass.getName();
19 System.out.println("Array: " + arrayName + ", Component: " + componentName);
20 int length = Array.getLength(source);
21 Object ret = Array.newInstance(componentClass, length);
22 System.arraycopy(source, 0, ret, 0, length);
23 return ret;
24 }
25 }
26 /* 输出结果如下:
27 Array: [I, Component: int
28 The both array are equal.
29 */
3. 基于对象域字段的反射:
1) 列出对象的public域字段和所有声明的域字段(不包含超类的):
1 public class MyTest {
2 public static void main(String args[]) {
3 Class<Point> cls = java.awt.Point.class;
4 Field[] fieldPublic = cls.getFields();
5 System.out.println("Here are public fields.");
6 for (Field f : fieldPublic) {
7 System.out.println(f.getType());
8 }
9 Field[] fieldDeclared = cls.getDeclaredFields();
10 System.out.println("Here are all declared fields including private "
11 + "and static and protect.");
12 for (Field f : fieldDeclared) {
13 System.out.println(f.getType());
14 }
15 }
16 }
17 /* 输出结果如下:
18 Here are public fields.
19 int
20 int
21 Here are all declared fields including private and static and protect.
22 int
23 int
24 long
25 */
2) 基于域字段的字符串名称获取该域字段的值:
1 public class MyTest {
2 public static void main(String args[]) throws Exception {
3 Object o = new TestClass();
4 //根据域字段的字符串名字反射出与该字段对应Field类对象。
5 Field field = o.getClass().getField("firstValue");
6 //获取该域字段类型的Class对象。
7 Class<?> type = field.getType();
8 //根据域字段的类型,调用Field.getXxx()方法获取该对象域字段的值。
9 if (type.toString().equals("double"))
10 System.out.println(field.getDouble(o));
11 else if (type.toString().equals("int"))
12 System.out.println(field.getInt(o));
13 }
14 }
15
16 class TestClass {
17 public double firstValue = 3.14;
18 }
19 /* 输出结果如下:
20 3.14
21 */
3) 获取和设置指定域字段的值:
1 public class MyTest {
2 public static void main(String args[]) throws Exception {
3 Bean demo = new Bean();
4 Class<? extends Bean> cl = demo.getClass();
5
6 Field field = cl.getField("id");
7 field.set(demo, new Long(10));
8 Object value = field.get(demo);
9 System.out.println("Value = " + value);
10
11 field = cl.getField("now");
12 field.set(null, new Date());
13 value = field.get(null);
14 System.out.println("Value = " + value);
15 }
16 }
17 class Bean {
18 public static Date now;
19 public Long id;
20 public String name;
21 }
22 /* 输出结果如下:
23 Value = 10
24 Value = Sun Sep 04 11:38:15 CST 2011
25 */
4. 泛型信息的反射:
1) 获取类的泛型接口信息
1 public static void main(String args[]) throws Exception {
2 Class<?> c = Class.forName("java.util.ArrayList");
3 System.out.format("Class:%n %s%n", c.getCanonicalName());
4 System.out.format("Modifiers:%n %s%n",
5 Modifier.toString(c.getModifiers()));
6
7 System.out.format("Type Parameters:%n");
8 //获取该泛型类的类型参数数组
9 TypeVariable[] tv = c.getTypeParameters();
10 if (tv.length != 0) {
11 System.out.format(" ");
12 for (TypeVariable t : tv)
13 System.out.format("%s ", t.getName());
14 System.out.println();
15 } else {
16 System.out.format(" -- No Type Parameters --%n%n");
17 }
18
19 System.out.format("Implemented Interfaces:%n");
20 //获取该类实现的接口,如果实现的接口为泛型接口,则打印出他的类型参数。
21 //getInterfaces()不会打印出类型参数。
22 Type[] intfs = c.getGenericInterfaces();
23 if (intfs.length != 0) {
24 for (Type intf : intfs)
25 System.out.format(" %s%n", intf.toString());
26 System.out.format("%n");
27 } else {
28 System.out.format(" -- No Implemented Interfaces --%n%n");
29 }
30 }
31 /* 输出结果如下:
32 Class:
33 java.util.ArrayList
34 Modifiers:
35 public
36 Type Parameters:
37 E
38 Implemented Interfaces:
39 java.util.List<E>
40 interface java.util.RandomAccess
41 interface java.lang.Cloneable
42 interface java.io.Serializable
43 */
2) 获取类的泛型接口、接口、泛型超类和超类信息的比较:
1 public static void main(String args[]) throws Exception {
2 //1. 超类
3 Class<?> ts = TreeMap.class.getSuperclass();
4 System.out.println(ts + "\n");
5 //2. 如果超类为泛型类,输出该泛型超类的类型信息。
6 Type t = TreeMap.class.getGenericSuperclass();
7 System.out.println(t + "\n");
8 //3. 接口
9 Class<?>[] is = TreeMap.class.getInterfaces();
10 for (int i = 0; i < is.length; i++) {
11 System.out.println(is[i]);
12 }
13 System.out.println();
14 //2. 如果接口为泛型接口,输出该泛型接口的类型信息。
15 Type[] ts2 = TreeMap.class.getGenericInterfaces();
16 for (int i = 0; i < ts2.length; i++) {
17 System.out.println(ts2[i]);
18 }
19 }
20 /* 输出结果如下:
21 class java.util.AbstractMap
22
23 java.util.AbstractMap<K, V>
24
25 interface java.util.NavigableMap
26 interface java.lang.Cloneable
27 interface java.io.Serializable
28
29 java.util.NavigableMap<K, V>
30 interface java.lang.Cloneable
31 interface java.io.Serializable
32 */
3) 输出域方法的签名信息,包括返回值和参数列表的泛型类型信息:
1 import static java.lang.System.out;
2 public class MyTest {
3 private static final String fmt = "%24s: %s%n";
4 public static void main(String args[]) throws Exception {
5 Class<?> c = Class.forName("java.util.ArrayList");
6 Method[] allMethods = c.getDeclaredMethods();
7 for (Method m : allMethods) {
8 //1. 获取域方法的完整描述,如果是泛型方法,则会给出类型信息
9 out.format("%s%n", m.toGenericString());
10 //2. 获取返回值类型
11 out.format(fmt, "ReturnType", m.getReturnType());
12 //3. 获取返回值类型,如为泛型类型,则打印出类型信息
13 out.format(fmt, "GenericReturnType", m.getGenericReturnType());
14 //4. 获取参数列表
15 Class<?>[] pType = m.getParameterTypes();
16 //5. 获取参数列表,如为泛型参数,则打印出类型信息
17 Type[] gpType = m.getGenericParameterTypes();
18 for (int i = 0; i < pType.length; i++) {
19 out.format(fmt, "ParameterType", pType[i]);
20 out.format(fmt, "GenericParameterType", gpType[i]);
21 }
22 }
23 }
24 }
25 /* 输出结果如下(由于输出较长,这里只是给出有代表性的输出):
26 ... ...
27 public void java.util.ArrayList.add(int,E)
28 ReturnType: void
29 GenericReturnType: void
30 ParameterType: int
31 GenericParameterType: int
32 ParameterType: class java.lang.Object
33 GenericParameterType: E
34 ... ...
35 public <T> T[] java.util.ArrayList.toArray(T[])
36 ReturnType: class [Ljava.lang.Object;
37 GenericReturnType: T[]
38 ParameterType: class [Ljava.lang.Object;
39 GenericParameterType: T[]
40 ... ...
41 */
4) 输出对象域字段的类型信息:
1 public class MyTest<T> {
2 public String name = "Alice";
3 public List<Integer> list;
4 public T val;
5
6 public static void main(String args[]) throws Exception {
7 Class<?> c = Class.forName("MyTest");
8 for (Field f : c.getFields()) {
9 //getType和getGenericType之间的差异和上面用到的
10 //getReturnType和getGenericReturnType之间的差别相同
11 System.out.format("Type: %s%n", f.getType());
12 System.out.format("GenericType: %s%n", f.getGenericType());
13 }
14 }
15 }
16 /* 输出结果如下:
17 Type: class java.lang.String
18 GenericType: class java.lang.String
19 Type: interface java.util.List
20 GenericType: java.util.List<java.lang.Integer>
21 Type: class java.lang.Object
22 GenericType: T
23 */
5. 枚举的反射:
1) 获取枚举的常量列表:
1 import static java.lang.System.out;
2 public class MyTest {
3 public static void main(String args[]) throws Exception {
4 //判断该类是否为枚举
5 if (!Eon.class.isEnum())
6 return;
7 Class<?> c = Eon.class;
8 out.format("Enum name: %s%nEnum constants: %s%n", c.getName(),
9 Arrays.asList(c.getEnumConstants()));
10 Eon[] vs = Eon.values();
11 for (Eon e : vs) {
12 out.println("The name is " + e.name() + "\tThe ordinal is " + e.ordinal());
13 }
14 }
15 }
16
17 enum Eon {
18 HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC
19 }
20 /* 输出结果如下:
21 Enum name: Eon
22 Enum constants: [HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC]
23 The name is HADEAN The ordinal is 0
24 The name is ARCHAEAN The ordinal is 1
25 The name is PROTEROZOIC The ordinal is 2
26 The name is PHANEROZOIC The ordinal is 3
27 */
6. 域方法的反射:
1) 通过类构造器的反射对象构造新实例:
1 public static void main(String args[]) throws Exception {
2 //根据构造函数的参数列表获取Point类的带有该参数列表的构造函数的反射类
3 //该构造函数的原型为Point(int x,int y);
4 Constructor<Point> con = Point.class.getConstructor(new Class[] {
5 int.class, int.class });
6 //由于参数必须是Object的数组表示,因此对于Point构造的两个
7 //int参数,只能使用他们的包括类Integer.
8 Point obj = (Point) con.newInstance(new Object[] {
9 new Integer(123), new Integer(123) });
10 System.out.println(obj);
11 }
12 /* 输出结果如下:
13 java.awt.Point[x=123,y=123]
14 */
2) 通过域方法的名称和参数列表签名获取并执行该方法(静态方法):
1 public static void main(String args[]) throws Exception {
2 //通过方法名称和参数列表签名获取类的静态域方法。
3 Method m = Math.class.getMethod("sqrt", new Class[] { double.class });
4 //由于是静态方法,第一个参数传null,如是是非静态函数,可以
5 //该参数看成this引用,传递对象即可,后面的参数列表表示该
6 //反射方法的参数列表。这里Math.sqrt只有一个double类型的
7 //参数。invoke的返回值只能是Object类型,因此也只能先将其
8 //转换为原始类型的包装类型,在从包装类型获取原始类型。
9 Double o = (Double)m.invoke(null, 10);
10 System.out.println(o.doubleValue());
11 }
12 /* 输出结果如下:
13 3.1622776601683795
14 */
3) 通过域方法的名称和参数列表签名获取并执行该方法(非静态方法):
1 public class MyTest {
2 public static void main(String args[]) throws Exception {
3 String firstWord = "Hello ";
4 String secondWord = "everybody.";
5 String bothWords = append(firstWord, secondWord);
6 System.out.println(bothWords);
7 }
8
9 public static String append(String firstWord, String secondWord) {
10 String result = null;
11 Class<String> c = String.class;
12 //初始化域方法的参数类型列表
13 Class<?>[] parameterTypes = new Class[] { String.class };
14 Method concatMethod;
15 //初始化域方法的参数
16 Object[] arguments = new Object[] { secondWord };
17 try {
18 //根据域方法的名称和参数列表获取域方法的反射对象
19 concatMethod = c.getMethod("concat", parameterTypes);
20 //和静态方法的反射调用不同,这里的第一个参数必须填入,其作用
21 //相当于firstWord.concat(secondword);
22 result = (String) concatMethod.invoke(firstWord, arguments);
23 } catch (NoSuchMethodException e) {
24 System.out.println(e);
25 } catch (IllegalAccessException e) {
26 System.out.println(e);
27 } catch (InvocationTargetException e) {
28 System.out.println(e);
29 }
30 return result;
31 }
32 }
33 /* 输出结果如下:
34 Hello everybody.
35 */
4) 通过反射调用对象的私有域方法:
1 public class MyTest {
2 public static void main(String args[]) throws Exception {
3 TestClass tc = new TestClass();
4 Class<?> c = tc.getClass();
5 Method m = c.getDeclaredMethod("m");
6 //必须调用该方法通过反射的方法设置这个private的可访问性
7 //为true,否则调用时将会抛出IllegalAccessException异常
8 m.setAccessible(true);
9 Object o = m.invoke(tc);
10 }
11 }
12 class TestClass {
13 private void m() {
14 System.out.println("This is private method TestClass.m().");
15 }
16 }
17 /* 输出结果如下:
18 This is private method TestClass.m().
19 */
5) 通过栈帧获取当前方法的名称:
1 public static void main(String args[]) throws Exception {
2 System.out.println(new Exception().getStackTrace()[0].getMethodName());
3
4 //这里的第0帧为getStackTrace()
5 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName());
6 System.out.println(Thread.currentThread().getStackTrace()[1].getClassName());
7 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());
8 System.out.println(Thread.currentThread().getStackTrace()[1].getLineNumber());
9
10 }
11 /* 输出结果如下:
12 main
13 MyTest.java
14 MyTest
15 main
16 12
17 */
7. 基于反射的方法调用和普通的方法调用之间的效率差别:
我们在本篇开始的部分已经提到反射确实可以给我们的程序带来极大的灵活性,目前很多流行的框架都是非常依赖于Java提供的反射机制,反射几乎处处可见。然而这并不能成为我们滥用他的理由,还是那句话,没有免费的午餐,反射的灵活性是用极大的效率牺牲换来的,见下例:
1 public class MyTest {
2 public static void main(String args[]) throws Exception {
3 try {
4 final int CALL_AMOUNT = 1000000;
5 final MyTest ri = new MyTest();
6 int idx = 0;
7 //1. 直接使用正常的方法调用(没有反射)
8 long millis = System.currentTimeMillis();
9 for (idx = 0; idx < CALL_AMOUNT; ++idx)
10 ri.getValue();
11 System.out.println("Calling method " + CALL_AMOUNT
12 + " times programatically took "
13 + (System.currentTimeMillis() - millis) + " millis");
14
15 //2. 通过反射调用域方法,而且每次都重新获取该域方法的反射类。
16 millis = System.currentTimeMillis();
17 for (idx = 0; idx < CALL_AMOUNT; idx++) {
18 Method md = ri.getClass().getMethod("getValue", null);
19 md.invoke(ri, null);
20 }
21 System.out.println("Calling method " + CALL_AMOUNT
22 + " times reflexively with lookup took "
23 + (System.currentTimeMillis() - millis) + " millis");
24
25 //3.通过反射调用域方法,但是该方法的反射对象并不是每次都
26 //重新获取,而是重复使用,只是在域方法调用的时候通过反射完成。
27 Method md = ri.getClass().getMethod("getValue", null);
28 millis = System.currentTimeMillis();
29 for (idx = 0; idx < CALL_AMOUNT; idx++)
30 md.invoke(ri, null);
31 System.out.println("Calling method " + CALL_AMOUNT
32 + " times reflexively with cache took "
33 + (System.currentTimeMillis() - millis) + " millis");
34 } catch (final NoSuchMethodException ex) {
35 throw new RuntimeException(ex);
36 } catch (final InvocationTargetException ex) {
37 throw new RuntimeException(ex);
38 } catch (final IllegalAccessException ex) {
39 throw new RuntimeException(ex);
40 }
41 }
42 public String getValue() {
43 return this.value;
44 }
45 private String value = "some value";
46 }
47 /* 输出结果如下:
48 Calling method 1000000 times programatically took 0 millis
49 Calling method 1000000 times reflexively with lookup took 1422 millis
50 Calling method 1000000 times reflexively with cache took 110 millis
51 */