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.

至此,我们可以指定泛型其实就是为了防止错误输入的,它并不神秘,它只在编译阶段有效,绕过编译就无效了。