您的位置 首页 java

Java高级编程——反射之数组参数类型相关的坑爹面试题

问:下面程序段运行时注释行有没有问题?为什么?

public class Test { public void func(String[] args) { System.out.println((args == null) ? "null" : args.length); } public static void main(String[] args) throws Exception { Test obj = new Test(); Method m = obj.getClass().getMethod("func", String[].class); m.invoke(obj, new String[1]); //1 m.invoke(obj, new Object[]{new String[] {"a", "b"}}); //2 m.invoke(obj, (Object) new String[] {"a", "b"}); //3 m.invoke(obj, new String[] {"a"}); //4 m.invoke(obj, new String[] {"a", "b"}); //5 m.invoke(obj, new String[2]); //6 }} 

答: 上面程序的运行结果如下。

null22Exception in thread "main"  java . lang .IllegalArgumentException: argument type mismatch at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at Test.main(Test.java:15)Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at Test.main(Test.java:16)Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)
 at Test.main(Test.java:17) 

对于注释1来说 new String[1] 传递给 invoke 方法的可变参数时实质被拆解成了一个 null,即一个 Object,所以注释1反射成功且打印为 null。

对于注释2来说和注释1类似,区别就是2中传递给 invoke 方法的可变参数实质是 new 的 Object 数组,而这里的 Object 数组只有一个元素为 new String[]{“a”, “b”},所以注释2反射成功且接收到了参数为 String 数组的元素,故而打印为 2。

对于注释3来说强制转换为 Object 类型就相当于传递给 invoke 方法的可变参数为一个参数,且参数值为 new String[]{“a”, “b”},所以能反射成功且打印值为 2。

对于注释4来说抛出参数类型不匹配是合理的,因为 new String[]{“a”} 在传递给 invoke 方法时实质上被拆解成了一个值为 “a” 的参数,即 String 类型,所以在反射时由于 func 方法需要的是一个 String[] 数组类型,而传入的却是一个字符串,所以抛出类型不匹配异常(注意与注释1对比理解,null 可以是任意对象类型的值)。

对于注释5和注释6来说比较类同,都是在传递给 invoke 方法时变成了两个参数,而反射 func 需要一个参数,故而反射时抛出异常,提示参数个数非法异常。

接着上面的问题再想一种情况,假设调用语句是 m.invoke(obj, new String[] {}); 时运行结果会怎么样呢?这种情况其实一样会报错 IllegalArgumentException: wrong number of arguments,因为 new String[] {} 只是初始化了长度为 0 的数组,传递给 invoke 变长参数解析时就会因参数个数不匹配抛出异常。

问:下面程序段运行时注释行有没有问题?为什么?

public class Test { public void func(String key, String[] args) { System.out.println((args == null) ? "null" : args.length); } public static void main(String[] args) throws Exception { Test obj = new Test(); Method m = obj.getClass().getMethod("func", String.class, String[].class); m.invoke(obj, new String(), new String[1]); //1 m.invoke(obj, new String(), new Object[]{new String[] {"a", "b"}}); //2 m.invoke(obj, new String(), (Object) new String[] {"a", "b"}); //3 m.invoke(obj, new String(), new String[] {"a"}); //4 m.invoke(obj, new String(), new String[] {"a", "b"}); //5 m.invoke(obj, new String(), new String[2]); //6 }} 

答: 上面程序的运行结果如下。

1Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at Test.main(Test.java:14)2122 

对于注释1来说由于 invoke 第二个参数是一个 Object 类型的 args 变长参数,而注释1中传递给 invoke 的 args 已经明确是两个实参了,且解析后 invoke 的 args 的第二个参数是一个 String 数组,故完全符合 func 参数类型,从而可以正常运行打印数组长度为 1。

对于注释2来说和注释1传递给 invoke 类似,只是这时候传递给 func 的第二个参数变成了 Object 数组,不是 String 数组,所以类型不匹配从而抛出参数类型不匹配异常。

对于注释3的 invoke 变长参数拆分解析是一样的,只不过传递给的参数被强转成了 Object,而 Object 是任意对象类型的基类,且被强转类型实质是 String 数组,所以在运行时不存在影响,故而打印数组长度为2。

对于注释4、5、6和注释1完全一样,仅仅可以理解成 String 数组的元素值不同而已。

接着上面的问题再想一种情况,假设调用语句是 m.invoke(obj, new String(), new String[] {}); 时运行结果会怎么样呢?这种情况打印数组长度为0,原因类似注释1。

所以可以看出,其实在反射参数是数组类型时要特别注意变长参数解析问题,因为上面的问题实质都是 Java 的变长参数问题,关于变长参数问题会有专门的推送解析相关题目。

这里我们只要记住一个规则即可:当 Java 方法中只有一个参数是数组,反射的时候我们不能想当然的传递数组进去,传数组进去的时候会被当作多个参数,我们应该把数组转为 Object,这样才表示一个参数,具体原因就是 Java 变长参数的特性原理。

文章来源:智云一二三科技

文章标题:Java高级编程——反射之数组参数类型相关的坑爹面试题

文章地址:https://www.zhihuclub.com/200472.shtml

关于作者: 智云科技

热门文章

网站地图