1.通过前面的学习,自己感觉其实反射机制也没有很神秘,无非就是类本身就是一个对象,通过类的类类型去动态加载类。
2.今天学习类的方法的反射操作以及通过反射来了解集合泛型的本质。
A.方法的反射操作:
首先我们创建一个类
class A{
public void print(int a,int b){
System.out.println(a+b);
}
public void print(String a, String b){
System.out.println(a.toUpperCase()+","+b.toLowerCase());
}
}
接着创建测试类
public class ClassDemo2 {
public static void main(String[] args) {
A a1 = new A();
Class c = a1.getClass();
try {
// Method m = c.getMethod("print", new Class[]{int.class,int.class});
Method m = c.getMethod("print", int.class,int.class);
//方法的反射操作
//a1.print(10, 20); 方法的反射操作是用m对象来进行方法调用 和a1.print调用的效果完全相同
//方法如果没有返回值返回null,有返回值返回具体的返回值
Object o = m.invoke(a1,new Object[]{10,20});
System.out.println("******************");
//获取方法print(String,String)
Method mt = c.getMethod("print", String.class,String.class);
o = mt.invoke(a1, "abcA","Acv");
} catch (Exception e) {
e.printStackTrace();
}
}
}
在测试类中有详细的注释:那么是什么意思呢,简单理解
本来调用类A中的print()方法时,我们得通过new A()创建A类的对象才能调用print()方法,也就是A a = new A();
a.print(10,20);
而学习了反射后,我们发现调用print()方法还有别的方式
1.我们首先通过Class c = a.getClass();
获取a对象的类类型。
2.通过Method m = c.getMethod("print",int.class,int.class);
获取print方法。
public Method getMethod(String name, Class<?>... parameterTypes)
第一个参数代表 你所要获取的方法名;第二个是多参类型,表示你所要获取的方法的参数列表的参数类类型。
接着通过invoke方法来执行方法的反射操作
//方法如果没有返回值返回null,有返回值返回具体的返回值
//第一个参数是所要执行方法操作的对象,后面则是方法参数列表所要传的实际参数
Object o = m.invoke(a1,new Object[]{10,20});
至此,方法的反射操作即完成了。当然如果想调用A类的print(String a, String b)方法也是一样的操作了
//获取方法print(String,String)
Method mt=c.getMethod("print",String.class,String.class);
o = mt.invoke(a1, "abcA","Acv");
B.通过反射了解集合泛型的本质:
完整代码如下:
public class ClassDemo3 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
ArrayList<String> list1 = new ArrayList<String>();
list1.add("hello");
//list1.add(20); 错误的,因为规定了类型为String
Class c1 = list.getClass();
Class c2 = list1.getClass();
System.out.println(c1 == c2);
//反射的操作都是编译之后的操作
/** * c1==c2结果返回true 说明编译之后集合的泛型是去泛型化的 * java中集合的泛型,是防止错误输入的,只在编译阶段有效 * 绕过编译就无效了 * 验证:我们可以通过方法的反射操作来绕过编译 */
try {
Method m = c1.getMethod("add", Object.class);
m.invoke(list1, 100);
System.out.println(list1.size());
System.out.println(list1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
很简短的几行代码,但是让我更深的理解了泛型的本质.
为什么//list1.add(20);
这个是错误的呢,首先list1定义了泛型,String类型,那么在编译时,相当于给你规定了类型,必须add String类型的参数才行,所以当然是不能add 20的,但是 c1是等于c2的,为什么呢,因为泛型在编译之后就被去掉了,也就是说ArrayList<String> list1 = new ArrayList<String>();
这行代码在编译后就变成了ArrayList list1 = new ArrayList();
所以c1最后是等于c2的。
而当我们用反射来操作add方法时,却可以添加20,这是因为反射的操作是在编译之后执行的,因此,编译之后不存在泛型,所以可以添加20.
至此,我们可以指定泛型其实就是为了防止错误输入的,它并不神秘,它只在编译阶段有效,绕过编译就无效了。