引言

这篇文章是反射系列的最后一篇,我们来看Method和Constructor这两个类的关键方法。

Method关键方法

invoke方法

invoke应该是Method用的最多的方法了,它的实现如下:

  1. @CallerSensitive
  2. public Object invoke(Object obj, Object... args)
  3. throws IllegalAccessException, IllegalArgumentException,
  4. InvocationTargetException
  5. {
  6. if (!override) {
  7. if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
  8. Class<?> caller = Reflection.getCallerClass();
  9. checkAccess(caller, clazz, obj, modifiers);
  10. }
  11. }
  12. MethodAccessor ma = methodAccessor; // read volatile
  13. if (ma == null) {
  14. ma = acquireMethodAccessor();
  15. }
  16. return ma.invoke(obj, args);
  17. }

obj是该方法所在的对象,如果这个方法是一个static方法,那么obj可以为null,args是该方法执行需要的参数,返回值Object是执行该方法的返回值。
与Field的get和set方法一样,Method的Invoke方法同样需要首先校验override,看是否要进行访问控制权限的校验。所以当我们不清楚一个方法的访问权限但是需要执行这个方法的时候,就需要先调用setAccessible(true)来忽略掉访问控制权限的校验。
看下面的例子:

  1. public class MethodTest {
  2. private int getNumber(String s,String s1,int count){
  3. return s.length() + s1.length() + count;
  4. }
  5. }
  6. public class MethodTestCaller {
  7. public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  8. Class<MethodTest> methodTestClass = MethodTest.class;
  9. Method getNumber = methodTestClass.getDeclaredMethod("getNumber", String.class, String.class, int.class);
  10. int aNumber = (int) getNumber.invoke(new MethodTest(), "string1", "string2", 10);
  11. System.out.println(aNumber);
  12. }
  13. }

我们在使用invoke调用private方法时没有忽略掉访问控制权限校验,会报错:

  1. Exception in thread "main" java.lang.IllegalAccessException: Class person.andy.concurrency.reflect.method.MethodTestCaller can not access a member of class person.andy.concurrency.reflect.method.MethodTest with modifiers "private"
  2. at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
  3. at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
  4. at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
  5. at java.lang.reflect.Method.invoke(Method.java:491)
  6. at person.andy.concurrency.reflect.method.MethodTestCaller.main(MethodTestCaller.java:10)

修改一下main方法:

  1. public class MethodTestCaller {
  2. public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  3. Class<MethodTest> methodTestClass = MethodTest.class;
  4. Method getNumber = methodTestClass.getDeclaredMethod("getNumber", String.class, String.class, int.class);
  5. getNumber.setAccessible(true);
  6. int aNumber = (int) getNumber.invoke(new MethodTest(), "string1", "string2", 10);
  7. System.out.println(aNumber);
  8. }
  9. }

输出:

  1. 24

除了invoke方法,Method的其他方法在实际开发中用到的就不是很多了,但是有几个还是比较有意思的,我们来看一下:

isBridge方法

  1. public boolean isBridge() {
  2. return (getModifiers() & Modifier.BRIDGE) != 0;
  3. }

这个方法判断一个方法是否是桥接方法。桥接方法我们在使用桥接方法来保护多态这里已经讲过。

  1. public class BridgeTest<T> {
  2. private T t;
  3. public T getT() {
  4. return t;
  5. }
  6. public void setT(T t) {
  7. this.t = t;
  8. }
  9. public static void main(String[] args) throws NoSuchMethodException {
  10. Class<BridgeTestSub> bridgeTestSubClass = BridgeTestSub.class;
  11. Method setT = bridgeTestSubClass.getMethod("setT", Object.class);
  12. System.out.println(setT.isBridge());
  13. }
  14. }
  15. public class BridgeTestSub extends BridgeTest<SimpleObject> {
  16. @Override
  17. public void setT(SimpleObject simpleObject) {
  18. super.setT(simpleObject);
  19. }
  20. }

输出结果:

  1. true

证明setT(Object object)是一个桥接方法。
Method的方法就重点讲这两个。接下来我们看Constructor的方法。

Constructor关键方法

newInstance方法

newInstance应该是Constructor用的最多的方法了,它用来创建一个类的实例。

  1. @CallerSensitive
  2. public T newInstance(Object ... initargs)
  3. throws InstantiationException, IllegalAccessException,
  4. IllegalArgumentException, InvocationTargetException
  5. {
  6. if (!override) {
  7. if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
  8. Class<?> caller = Reflection.getCallerClass();
  9. checkAccess(caller, clazz, null, modifiers);
  10. }
  11. }
  12. if ((clazz.getModifiers() & Modifier.ENUM) != 0)
  13. throw new IllegalArgumentException("Cannot reflectively create enum objects");
  14. ConstructorAccessor ca = constructorAccessor; // read volatile
  15. if (ca == null) {
  16. ca = acquireConstructorAccessor();
  17. }
  18. @SuppressWarnings("unchecked")
  19. T inst = (T) ca.newInstance(initargs);
  20. return inst;
  21. }

参数是创建这个类的实例需要的参数,也就是构造方法的参数。从代码逻辑可以看出newInstance方法首先要判断是否需要进行访问控制权限的校验,与Method的invoke方法、Field的get方法等是一样的。

  1. if ((clazz.getModifiers() & Modifier.ENUM) != 0)
  2. throw new IllegalArgumentException("Cannot reflectively create enum objects");

这段代码需要重点了解一下,它反映了枚举类的对象不能通过newInstance(实际上是反射)来创建的这一事实。这个我们在枚举的分析这篇文章也讲解过。
返回值就是创建的类的实例对象。我们这里不再列举这个方法的具体实例了。
Method和Constructor还有很多其他的方法,这里没有一一列举,有兴趣的同学可以自己查看源码。

小结

到此为止,反射系列的文章就结束了。通过这几篇文章,我们已经对反射体系中重要的接口和类应该很清楚了,例如Type、AnnotatedElement、GenericDeclaration、Member、Executable、AccessibleObject等,知道它们的定义和方法的含义。
在类层次结构的基础上,我们重点分析了Class、Field、Constructor、Method这四个类的关键方法,作为反射体系中被用到最多的四个类,理解它们的继承关系和关键方法的使用,对理解java的反射特性至关重要。