昨天在群里跟大家讨论了下java反射调用可变参数的问题,这个问题起因是我们需要反射调用另一个部门提供的方法,我同事说java不能反射调用可变参数的方法,于是我写了个demo证明了他这个观点的错误。但是测试过程中,有一点我不明白,就是反射调用可变参数的方法时,为什么一定要保证传入的参数数组长度为1,在群里跟大家讨论了很多,没有得到确切的答案,参照网上大牛写的东西和我自己跟源码的过程,记录如下:

1.两个类,一个父类,一个子类

1	package com.reflect.test;
2	 
3	public class BaseObject {
4	     
5	    public void getObjectName(){
6	        System.out.println("BaseObject");
7	    }
8	 
9	}
01	package com.reflect.test;
02	 
03	public class SubObject extends BaseObject{
04	    @Override
05	    public void getObjectName() {
06	        System.out.println("SubObject");
07	    }
08	    public void getParamsLength(String...params){
09	        System.out.println("param's length is:"+params.length);
10	    }
11	    public void getParamsLength(String param1,String param2){
12	        System.out.println(param1 + "-" + param2);
13	    }
14	}

2.测试类,主要测试重载方法的调用、可变参数方法的调用、定参方法的调用

01	package com.reflect.test;
02	 
03	import java.lang.reflect.Method;
04	 
05	public class ReflectTest {
06	     
07	    private static final String BASE_OBJECT_PATH = "com.reflect.test.BaseObject";
08	    private static final String SUB_OBJECT_PATH = "com.reflect.test.SubObject";
09	     
10	    public static void main(String[] args) throws Exception{
11	         
12	        Class<?> bClazz = Class.forName(BASE_OBJECT_PATH);
13	        Class<?> sClazz = Class.forName(SUB_OBJECT_PATH);
14	         
15	        Object bObj = bClazz.newInstance();//父类实例
16	        Object sObj = sClazz.newInstance();//子类实例
17	         
18	        //1.反射调用子类父类的重载方法
19	        //多态+动态绑定
20	        Method bMethod = bClazz.getDeclaredMethod("getObjectName");
21	        bMethod.invoke(bObj);//父类的bMethod调用父类的getObjectName()
22	        bMethod.invoke(sObj);//父类的bMethod调用子类的getObjectName();
23	         
24	        Method sMethod = sClazz.getDeclaredMethod("getObjectName");
25	        //不符合多态和动态绑定
26	        //sMethod.invoke(bObj);//sMethod调用父类的getObjectName(),会报错:java.lang.IllegalArgumentException: object is not an instance of declaring class
27	        sMethod.invoke(sObj);
28	         
29	        //2.反射调用可变参数的方法
30	        Method changeMethod = sClazz.getDeclaredMethod("getParamsLength", String[].class);
31	        //可变参数必须这样封装,因为java反射内部实现做了参数个数为1的判断,如果参数长度不为1,则会抛出异常
32	        String[] strParams = {"a","b","c"};
33	        Object[] cParams = {strParams};
34	        changeMethod.invoke(sObj, cParams);
35	         
36	        //3.反射调用固定长度参数的方法
37	        Method unChangeMethod1 = sClazz.getDeclaredMethod("getParamsLength", String.class,String.class);
38	        unChangeMethod1.invoke(sObj, "Hello","Java");
39	        //也可以写成这样
40	        Class<?>[] clazzs = {String.class,String.class};
41	        Method unChangeMethod2 = sClazz.getDeclaredMethod("getParamsLength", clazzs);
42	        unChangeMethod2.invoke(sObj, "Hello","Java");
43	        //下面的这种调用形式也是可以的,不过会报警告
44	        //String[] params1 = {"Hello","Java"};
45	        //unChangeMethod1.invoke(sObj, params1);
46	    }
47	}

下面是JDK里面Method 的invoke方法的源码

从代码中可以看出,先检查 AccessibleObject的override属性是否为true(override属性默认为false)。AccessibleObject是Method,Field,Constructor的父类,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直接调用。

如果不是ture,则要进行访问权限检测。用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass()方法获得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的结果。

01	package com.reflect.test;
02	 
03	import java.lang.reflect.Method;
04	 
05	public class ReflectTest {
06	     
07	    private static final String BASE_OBJECT_PATH = "com.reflect.test.BaseObject";
08	    private static final String SUB_OBJECT_PATH = "com.reflect.test.SubObject";
09	     
10	    public static void main(String[] args) throws Exception{
11	         
12	        Class<?> bClazz = Class.forName(BASE_OBJECT_PATH);
13	        Class<?> sClazz = Class.forName(SUB_OBJECT_PATH);
14	         
15	        Object bObj 01	@CallerSensitive
02	    public Object invoke(Object obj, Object... args)
03	        throws IllegalAccessException, IllegalArgumentException,
04	           InvocationTargetException
05	    {
06	        if (!override) {
07	            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
08	                // Until there is hotspot @CallerSensitive support
09	                // can't call Reflection.getCallerClass() here
10	                // Workaround for now: add a frame getCallerClass to
11	                // make the caller at stack depth 2
12	                Class<?> caller = getCallerClass();
13	                checkAccess(caller, clazz, obj, modifiers);
14	            }
15	        }
16	        MethodAccessor ma = methodAccessor;             // read volatile
17	        if (ma == null) {
18	            ma = acquireMethodAccessor();
19	        }
20	        return ma.invoke(obj, args);
21	    }
22	 
23	//验证的代码,securityCheckCache就是JDK做的缓存
24	 volatile Object securityCheckCache;
25	 
26	    void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers)
27	        throws IllegalAccessException
28	    {
29	        if (caller == clazz) {  // quick check
30	            return;             // ACCESS IS OK
31	        }
32	        Object cache = securityCheckCache;  // read volatile
33	        Class<?> targetClass = clazz;
34	        if (obj != null
35	            && Modifier.isProtected(modifiers)
36	            && ((targetClass = obj.getClass()) != clazz)) {
37	            // Must match a 2-list of { caller, targetClass }.
38	            if (cache instanceof Class[]) {
39	                Class<?>[] cache2 = (Class<?>[]) cache;
40	                if (cache2[1] == targetClass &&
41	                    cache2[0] == caller) {
42	                    return;     // ACCESS IS OK
43	                }
44	                // (Test cache[1] first since range check for [1]
45	                // subsumes range check for [0].)
46	            }
47	        } else if (cache == caller) {
48	            // Non-protected case (or obj.class == this.clazz).
49	            return;             // ACCESS IS OK
50	        }
51	 
52	        // If no return, fall through to the slow path.
53	        slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
54	    }= bClazz.newInstance();//父类实例
16	        Object sObj = sClazz.newInstance();//子类实例
17	         
18	        //1.反射调用子类父类的重载方法
19	        //多态+动态绑定
20	        Method bMethod = bClazz.getDeclaredMethod("getObjectName");
21	        bMethod.invoke(bObj);//父类的bMethod调用父类的getObjectName()
22	        bMethod.invoke(sObj);//父类的bMethod调用子类的getObjectName();
23	         
24	        Method sMethod = sClazz.getDeclaredMethod("getObjectName");
25	        //不符合多态和动态绑定
26	        //sMethod.invoke(bObj);//sMethod调用父类的getObjectName(),会报错:java.lang.IllegalArgumentException: object is not an instance of declaring class
27	        sMethod.invoke(sObj);
28	         
29	        //2.反射调用可变参数的方法
30	        Method changeMethod = sClazz.getDeclaredMethod("getParamsLength", String[].class);
31	        //可变参数必须这样封装,因为java反射内部实现做了参数个数为1的判断,如果参数长度不为1,则会抛出异常
32	        String[] strParams = {"a","b","c"};
33	        Object[] cParams = {strParams};
34	        changeMethod.invoke(sObj, cParams);
35	         
36	        //3.反射调用固定长度参数的方法
37	        Method unChangeMethod1 = sClazz.getDeclaredMethod("getParamsLength", String.class,String.class);
38	        unChangeMethod1.invoke(sObj, "Hello","Java");
39	        //也可以写成这样
40	        Class<?>[] clazzs = {String.class,String.class};
41	        Method unChangeMethod2 = sClazz.getDeclaredMethod("getParamsLength", clazzs);
42	        unChangeMethod2.invoke(sObj, "Hello","Java");
43	        //下面的这种调用形式也是可以的,不过会报警告
44	        //String[] params1 = {"Hello","Java"};
45	        //unChangeMethod1.invoke(sObj, params1);
46	    }
47	}


然后就是调用MethodAccessor的invoke方法了。

调用MethodAccessor的invoke方法。每个Method对象包含一个root对象,root对象里持有一个MethodAccessor对象。这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化。代码片段如下;

01	//Method类中的代码片段,生成MethodAccessor
02	private volatile MethodAccessor methodAccessor;
03	private Method       root;
04	private MethodAccessor acquireMethodAccessor() {
05	        // First check to see if one has been created yet, and take it
06	        // if so
07	        MethodAccessor tmp = null;
08	        if (root != null) tmp = root.getMethodAccessor();
09	        if (tmp != null) {
10	            methodAccessor = tmp;
11	        } else {
12	            // Otherwise fabricate one and propagate it up to the root
13	            tmp = reflectionFactory.newMethodAccessor(this);
14	            setMethodAccessor(tmp);
15	        }
16	 
17	        return tmp;
18	    }
19	 
20	// reflectionFactory在父类AccessibleObject中定义,代码片段如下:
21	  static final ReflectionFactory reflectionFactory =
22	        AccessController.doPrivileged(
23	            new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());

ReflectionFactory生成MethodAccessor:如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor,否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引用。但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl了。代码片段如下:

01	public MethodAccessor newMethodAccessor(Method paramMethod) {
02	    checkInitted();
03	 
04	    if (noInflation) {
05	      return new MethodAccessorGenerator().generateMethod(paramMethod.getDeclaringClass(), paramMethod.getName(), paramMethod.getParameterTypes(), paramMethod.getReturnType(), paramMethod.getExceptionTypes(), paramMethod.getModifiers());
06	    }
07	 
08	    NativeMethodAccessorImpl localNativeMethodAccessorImpl = new NativeMethodAccessorImpl(paramMethod);
09	 
10	    DelegatingMethodAccessorImpl localDelegatingMethodAccessorImpl = new DelegatingMethodAccessorImpl(localNativeMethodAccessorImpl);
11	 
12	    localNativeMethodAccessorImpl.setParent(localDelegatingMethodAccessorImpl);
13	    return localDelegatingMethodAccessorImpl;
14	  }

MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。 为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。

看下NativeMethodAccessorImpl 中的invoke方法:

代码片段如下:

01	package sun.reflect;
02	 
03	import java.lang.reflect.InvocationTargetException;
04	import java.lang.reflect.Method;
05	 
06	class NativeMethodAccessorImpl extends MethodAccessorImpl
07	{
08	  private Method method;
09	  private DelegatingMethodAccessorImpl parent;
10	  private int numInvocations;
11	 
12	  NativeMethodAccessorImpl(Method paramMethod)
13	  {
14	    this.method = paramMethod;
15	  }
16	 
17	  public Object invoke(Object paramObject, Object[] paramArrayOfObject)
18	    throws IllegalArgumentException, InvocationTargetException
19	  {
20	    if (++this.numInvocations > ReflectionFactory.inflationThreshold()) {
21	      MethodAccessorImpl localMethodAccessorImpl = (MethodAccessorImpl)new MethodAccessorGenerator().generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
22	 
23	      this.parent.setDelegate(localMethodAccessorImpl);
24	    }
25	 
26	    return invoke0(this.method, paramObject, paramArrayOfObject);
27	  }
28	 
29	  void setParent(DelegatingMethodAccessorImpl paramDelegatingMethodAccessorImpl) {
30	    this.parent = paramDelegatingMethodAccessorImpl;
31	  }
32	 
33	  private static native Object invoke0(Method paramMethod, Object paramObject, Object[] paramArrayOfObject);
34	}

调用natiave方法invoke0执行方法调用.

注意这里有一个计数器numInvocations,每调用一次方法+1,当比 ReflectionFactory.inflationThreshold(15)大的时候,用MethodAccessorGenerator创建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。下一次DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的java字节码的MethodAccessor

每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。

注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的,是用C写的

为了验证这个结论,我故意写出一个非法参数,循环调用16次并catch下异常,结果如下:从结果中看出,前15次都是调用NativeMethodAccessorImpl,第16次开始就是调用DelegatingMethodAccessorImpl了。

01	java.lang.IllegalArgumentException: wrong number of arguments
02	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
03	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
04	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
05	    at java.lang.reflect.Method.invoke(Method.java:606)
06	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
07	java.lang.IllegalArgumentException: wrong number of arguments
08	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
09	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
10	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
11	    at java.lang.reflect.Method.invoke(Method.java:606)
12	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
13	java.lang.IllegalArgumentException: wrong number of arguments
14	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
15	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
16	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17	    at java.lang.reflect.Method.invoke(Method.java:606)
18	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
19	java.lang.IllegalArgumentException: wrong number of arguments
20	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
21	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
22	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
23	    at java.lang.reflect.Method.invoke(Method.java:606)
24	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
25	java.lang.IllegalArgumentException: wrong number of arguments
26	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
27	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
28	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
29	    at java.lang.reflect.Method.invoke(Method.java:606)
30	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
31	java.lang.IllegalArgumentException: wrong number of arguments
32	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
33	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
34	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
35	    at java.lang.reflect.Method.invoke(Method.java:606)
36	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
37	java.lang.IllegalArgumentException: wrong number of arguments
38	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
39	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
40	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
41	    at java.lang.reflect.Method.invoke(Method.java:606)
42	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
43	java.lang.IllegalArgumentException: wrong number of arguments
44	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
45	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
46	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
47	    at java.lang.reflect.Method.invoke(Method.java:606)
48	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
49	java.lang.IllegalArgumentException: wrong number of arguments
50	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
51	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
52	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
53	    at java.lang.reflect.Method.invoke(Method.java:606)
54	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
55	java.lang.IllegalArgumentException: wrong number of arguments
56	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
57	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
58	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
59	    at java.lang.reflect.Method.invoke(Method.java:606)
60	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
61	java.lang.IllegalArgumentException: wrong number of arguments
62	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
63	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
64	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
65	    at java.lang.reflect.Method.invoke(Method.java:606)
66	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
67	java.lang.IllegalArgumentException: wrong number of arguments
68	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
69	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
70	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
71	    at java.lang.reflect.Method.invoke(Method.java:606)
72	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
73	java.lang.IllegalArgumentException: wrong number of arguments
74	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
75	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
76	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
77	    at java.lang.reflect.Method.invoke(Method.java:606)
78	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
79	java.lang.IllegalArgumentException: wrong number of arguments
80	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
81	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
82	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
83	    at java.lang.reflect.Method.invoke(Method.java:606)
84	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
85	java.lang.IllegalArgumentException: wrong number of arguments
86	    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
87	    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
88	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
89	    at java.lang.reflect.Method.invoke(Method.java:606)
90	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
91	java.lang.IllegalArgumentException
92	    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
93	    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
94	    at java.lang.reflect.Method.invoke(Method.java:606)
95	    at com.reflect.test.ReflectTest.main(ReflectTest.java:44)

下面看看java版的DelegatingMethodAccessorImpl的实现:

01	package sun.reflect;
02	 
03	import java.lang.reflect.InvocationTargetException;
04	 
05	class DelegatingMethodAccessorImpl extends MethodAccessorImpl
06	{
07	  private MethodAccessorImpl delegate;
08	 
09	  DelegatingMethodAccessorImpl(MethodAccessorImpl paramMethodAccessorImpl)
10	  {
11	    setDelegate(paramMethodAccessorImpl);
12	  }
13	 
14	  public Object invoke(Object paramObject, Object[] paramArrayOfObject)
15	    throws IllegalArgumentException, InvocationTargetException
16	  {
17	    return this.delegate.invoke(paramObject, paramArrayOfObject);
18	  }
19	 
20	  void setDelegate(MethodAccessorImpl paramMethodAccessorImpl) {
21	    this.delegate = paramMethodAccessorImpl;
22	  }
23	 
24	 
25	package sun.reflect;
26	 
27	public class GeneratedMethodAccessor1 extends MethodAccessorImpl {   
28	    public GeneratedMethodAccessor1() {
29	        super();
30	    }
31	     
32	    public Object invoke(Object obj, Object[] args)  
33	        throws IllegalArgumentException, InvocationTargetException {
34	        // prepare the target and parameters
35	        if (obj == null) throw new NullPointerException();
36	        try {
37	            A target = (A) obj;
38	            if (args.length != 1) throw new IllegalArgumentException();
39	            String arg0 = (String) args[0];
40	        } catch (ClassCastException e) {
41	            throw new IllegalArgumentException(e.toString());
42	        } catch (NullPointerException e) {
43	            throw new IllegalArgumentException(e.toString());
44	        }
45	        // make the invocation
46	        try {
47	            target.foo(arg0);
48	        } catch (Throwable t) {
49	            throw new InvocationTargetException(t);
50	        }
51	    }
52	}

if (args.length != 1) throw new IllegalArgumentException();这一句就能解释我之前的疑问了,这块会判断参数数组的长度,如果长度不等于1,就会抛出非法参数的异常。

而且MethodAccessor会做强制类型转换再进行方法调用,但父类强制转化成子类的的时候就会报错类型不匹配错误了,所以如果变量的引用声明是父但实际指向的对象是子,那么这种调用也是可以的。