Java基础系列(二十三):反射进阶

2019 Java 开发者跳槽指南.pdf (吐血整理)….>>>

运行时使用反射

上一节中我们已经知道了如何查看任意对象的数据域名称和类型:

  • 获得对应的Class对象
  • 通过Class对象调用 getDeclaredFields()

那么我们如果想要在程序运行的时候获取数据域的具体值,应该怎么做呢?

问题的关键是使用Filed类的 get()方法,但是因为数据域是私有的,直接去访问这个数据域的话会抛出一个异常,反射机制的默认行为受限于Java的访问控制,如果一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制,为了达到这个目的我们可以使用 setAccessible()方法来做到,具体做法如下:

1
2
3
4
5
User user = new User("Li Ming", 20, 1998, 7, 29);
Class userClass = user.getClass();
Filed f = userClass.getDeclaredFiled("name");
f.setAccessible(true);
Object v = f.get(user); // v = "Li Ming"

如果我们希望为这个域赋予一个新值,可以使用 f.set(obj,value)方法将obj对象的f域设置为新值。

使用反射编写泛型数组

java.lang.reflect包中的Array类允许动态的创建数组。这里我们用到了Array类的静态方法newInstance,它能够构造新数组。在调用它的时候必须提供两个参数,一个是数组的元素类型,一个是数组的长度,数组的元素类型用于保证新创建的数组与原数组的类型保持一致。

具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
public static Object copyOf(Object a, int newLength) {
    Class c1 = a.getClass();
    if(!c1.isArray()) {
        return null;
    }
 
    Class componentType = c1.getComponentType();
    int length = Array.getLength(a);
    Object newArray = Array.newInstance(componentType, newLength);
    System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
    return newArray;
}

invoke方法

在Method类中有一个invoke方法,它允许调用包装在当前Method对象中的方法。invoke方法的签名是:

1
Object invoke(Object obj, Object... args);

第一个参数是隐式参数,其余的对象提供了显式参数,对于静态方法的话,第一个参数可以省略,设置为null。

如何得到相应的method对象呢?

这里需要用到Method类中的getMethod方法:

1
Method getMethod(String name, Class... parameterTypes)

第一个参数是方法的名称,第二个参数是方法的参数类型,这两个结合在一起就可以唯一确定一个方法,然后使用invoke进行调用。