Java反射之 getMethod() 与invoke的使用,int.class vs integer.class
1、forName 方法
forName是一个静态方法,其作用:通过调用来获取类名对应的Class对象,同时将Class对象加载进来。
如果将类名保存在字符串(如xml)中,就可以在程序运行时,动态调用加载。
注意:只有调用的参数是类名或者方法时,才可用。
2、newInstance()方法
作用:将对象实例化。返回类型为Object。与new的区别在于,new可以带参,而newInstance()不可以,一边初始化无参类。通常与forName()配合使用。
例:
-
String str = "java.util.Date";
-
Class cl1= Class.forName(str);//加载java.util.Date类
-
Object obj = cl1.newInstance();//实例化cl1
3、getMethod()方法
getMethod方法与getField方法类似,getField方法根据表示域名的字符串,返回一个Field对象。而getMethod方法则根据方法名称和相关参数,来定位需要查找的Method对象并返回。
getMethod与getDeclareMethods方法的区别在于,后者返回一个Method对象数组,需要自己在结果中查找所需Method对象。
原型: Method getMethod(String name,Class...parameterTypes)
参数解释:name: method的名称
parameterTypes:method的参数类型的列表(参数顺序需按声明method时的参数列表排列)
返回:符合method名称和参数的method对象
抛出错误:NoSuchMethodException
原因:没有找到所要查询的Method对象 或 Method名称为“<init>”或“<clinit>”
NullPointerException
原因:所要查询的Method对象的名称为null
SecurityException
原因:调用的类或其父类没有调用权限
例:
-
Method m1 = Employee.class.getMethod("getName");
-
Method m2 = Employee.class.getMethod("raiseSalary",double.class);
上面例子分别获得了Employee类的getName方法和raiseSalary方法的 方法指针m1,m2。
4、invoke方法
作用:调用包装在当前Method对象中的方法。
原型:Object invoke(Object obj,Object...args)
参数解释:obj:实例化后的对象
args:用于方法调用的参数
返回:根据obj和args调用的方法的返回值
抛出错误:IllegalAccessException
原因:Method对象强制Java语言执行控制 或 无权访问obj对象
IllegalArgumentException
原因:方法是实例化方法,而指定需要调用的对象并不是实例化后的类或接口
例:
-
Class l = Class.forName("test1.A");
-
Object obj1 = l.newInstance();
-
Object[] obj2 = new Object[1];
-
obj2[0] = new String("hello world");
-
Method m = l.getMethod("a1",new Class[] { String.class });
-
Object obj3 = m.invoke(obj1, obj2);
来自
java.lang.Class.isPrimitive
API有九个预定义的 Class 对象来表示八种基本类型和 void。它们由 Java 虚拟机创建,并且与它们所表示的基本类型具有相同的名称,即 boolean、byte、char、short、int、long、float 和 double。
这些对象只能通过以下公共静态最终变量
java.lang.Boolean.TYPE
、java.lang.Integer.TYPE
等访问对我来说,理解
int.class
和Integer.class
的最简单方法是停止认为Integer
是一个包装器(这意味着它是“特殊的”)。事实上,将 int 视为一种特殊情况更容易,也可能更合适。Integer 只是一个普通的 Java 类,与例如没有什么不同。细绳。它派生自
Object
,操作方式类似于Object
,在运行时您可以创建Integer
的实例,该实例采用类似对象的内存布局,例如开头有一个指向Integer.class
的指针,这就是 java.lang 的多态运行时行为的原因。Integer
实际上还没有什么特别的。如果你想象一个Java没有boolean、int、long这些原语,而只有Integer、Boolean、Long等,类型系统实际上是非常一致的。从概念上讲,您可以将
int
视为稍后出于性能原因引入的特殊类。最初,它与 Integer 无关。
在创建 Java 时,对于计算量大的程序来说,为普通数字维护类似对象的内存布局是非常昂贵的。而且大多数算术运算甚至根本不涉及多态动态调度。
例如。对数字调用诸如 toString 之类的方法的情况要少得多。int 是特殊的,因为它是一个类,其“实例”布置在内存中,并剥离了公共对象结构 - 只有四个连续字节,没有额外的元数据。
因此,您无法执行
123.getClass()
,因为 int 123 的运行时内存布局没有类指针。int.class
确实存在,但它与Integer.class
完全无关(还)。从某种意义上说, int 更类似于 Void ,因为 Void.class 确实存在,但你永远不能在o.class == Void.class
中拥有 object o 。Java就可以解决了,int就是int,Integer就是Integer。
但是人们意识到,虽然不太常见,但能够将 int 视为普通 Java 对象仍然非常有用,否则您将始终必须至少维护两组方法 - 一组接受普通对象,另一组接受普通对象采用原语 - 尽管性能不是您关心的问题。
在这些场景中,int 实际上表现得像 Integer。 Java 允许通过自动装箱过程自动进行这种转换,从而有效地使 int 和 Integer 相关。但 int 仍然是 int,Integer 仍然是 Integer(例如
int.class != Integer.class
)。但是 Integer.class 中引入了一个额外的字段来指示它与 int.class 相关,即Integer.TYPE
。所以Integer.TYPE == int.class
。对我来说Integer.TYPE
只是捕捉 Integer 和 int 相关的概念。如何使用它,或者它是否有用,都取决于你。实际上,int 和 Integer 在重要的地方仍然是不同的类型。例如:
void accept (int value); void accept (Integer value);
仍然被认为是两个重载。因此,在使用反射时,您可以使用 int.class 和 Integer.class 来区分两者。
当不使用反射或某种形式的元编程时,由于无法从对象导航到 int.class,因此在代码中使用 int.class 的情况相对较少。
有时会让人感到困惑,因为自动装箱语法似乎建议您应该获取 int.class:int value = 1; Class c = ((Object)value).getClass();
但这只是一种幻觉,因为当您执行
((Object)value)
时,您实际上是在创建一个具有相同值的新 Integer 实例。我唯一需要显式使用 int.class 和 Integer.class 的时候是构建一个泛型 api
<T> T getValue(String name, Class<T> type);
,其中我需要区分 api 是否允许返回 null:Class (Java Platform SE 8 ) (oracle.com)