反射基础知识第二篇

上一篇中讲了如何获取指定类的Class对象,及用Class对象获取该类的构造器、方法、成员变量,本篇主要探讨如何利用这些构造器、方法、成员变量等。

1.构造器,newInstance(Object ... initargs)方法

显而易见,利用构造器的目的就是产生一个对应类的实例:

public static void main(String[] args) {
    Class clazz = A.class;
    Constructor con = null;
    A a = null;
    try {
        con = clazz.getDeclaredConstructor(String.class, int.class);
        // con.setAccessible(true);
        a = (A) con.newInstance("张三", 19);
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println(a);
}

     如上,在获取到Constructor对象con之后,可以调用其newInstance(Object ... initargs)方法获取到对应类的对象。值得注意的是,如果构造器A(String name, int age)是private的话,那么如果调用的不是getDeclaredConstructor()而是getConstructor()的话,则会报NoSuchMethodException没有此方法异常;调用getConstructor()得到Constructor对象,这个对象对应一个私有化的构造器,这时候如果直接调用newInstance(Object ... initargs)方法的话,会报IllegalAccessException非法访问权限异常的。解决办法是在调用newInstance(Object ... initargs)方法之前先执行setAccessible(true)。setAccessible(boolean flag)方法是Constructor类、Method类、Field类的公共父类AccessibleObject的方法,也就是说如果构造器、方法的访问权限是private时,要想通过反射调用这个方法,需要在调用前执行setAccessible(true),这样就不会报非法访问权限异常了。

     但是,通常我们创建对应类对象的时候不会用Class对象获取完Constructor对象后再获取对应类实例,而是直接利用Class对象的newInstance()方法,通过这种方式生成对应类实例,其实是利用对应类的默认构造器,即无参构造器,所以对应类要有无参构造器(显示声明无参构造器或者根本就显示声明任何构造器),而且必须是public的,因为Class对象没有setAccessible()方法。

2.方法,public Object invoke(Object obj, Object... args):

invoke方法的调用者是一个Method对象,第一个参数是一个对应类的实例,后面参数是方法参数列表。可以看出,用invoke方法调用对应类某个方法的时候,就已经获得对应类的实例了,那为什么还要用invoke方法,而不是直接用对象调用呢?这是因为私有化的方法不能直接通过对象打点调用而只能通过invoke的方式调用:

public static void main(String[] args) {
    Class clazz = A.class;
    A a = null;
    Method method = null;
    try {
        method = clazz.getDeclaredMethod("say", String.class);
        a = (A) clazz.newInstance();
        method.setAccessible(true);
        method.invoke(a, "zhoujielun");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

如上,Method对象在调用invoke方法之前先执行setAccessible(true)就可以执行对应类的私有化方法了。本例中创建对应类实例的方法就是直接Class对象.newInstance(),而没有通过产生构造器对象,然后利用构造器对象再获取对应类实例的方法。

3.成员变量,public Object get(Object obj);public void set(Object obj, Object value)

get方法的参数是一个对应类的实例;

set方法的第一个参数是一个对应类的实例,第二个参数是要给这个实例的某个成员变量赋的值。

一个Field对象其实就绑定一个成员变量,在生成Field对象时就绑定了,如Field field=clazz.getDeclaredField("name");,此时field就绑定了name成员变量。

public static void main(String[] args) {
    Class clazz = A.class;
    A a = null;
    Constructor con = null;
    Field field = null;
    try {
        con = clazz.getDeclaredConstructor(String.class, int.class);
        a = (A) con.newInstance("张三", 19);
        field = clazz.getDeclaredField("name");

        String name = (String) field.get(a);
        System.out.println(name);

        field = clazz.getDeclaredField("age");
        field.setAccessible(true);
        int age = field.getInt(a);
        System.out.println(age);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

如上,如果在声明成员变量时时private的,则用Field对象操作成员变量之前要先执行setAccessible(true)。

原文地址:https://www.cnblogs.com/koushr/p/5873372.html