Java基础知识(二)反射

反射

反射机制提供了如下一些功能:

  • 在运行时分析类。

  • 在运行时查看对象。

  • 实现通用的数组操作代码。

  • 利用Method对象。

由于反射机制的权限较高,在利用其查看运行时错误时,也要注意是否破坏了程序的封装性。

Class类

实际上,Class负责保存类的信息,而不是对象。虚拟机利用运行时类型信息选择相应的方法执行。

Object类中的getClass方法会返回一个Class类型的实例。因此,可以令一个对象调用getClass方法来获取这个对象所属类的信息。其中最常见的类名可以通过getName方法来获取,类所在的包也包含在类名中。

// 假设e是某个类的实例
System.out.println(e.getClass().getName());

相反地,可以通过forName方法获取类名对应的Class实例。

// 这里要获取Random的Class实例,注意包名
Class cl = Class.forName("java.util.Random");
// 如果输入的类名不存在,会抛出“已检查异常”ClassNotFoundException。

还有一种很简单的方法来获取Class类对象,即直接在Java类型或void关键字后加上.class,获得匹配的类对象。

Class cl_1 = java.util.Random.class;
Class cl_2 = int.class;
Class cl_3 = Double[].class;

以上是获取Class类对象的三种方法。

获取Class类对象后,可以调用newInstance方法来调用类的默认构造器,创建类的一个实例。这里要求这个类必须有默认的构造器,否则会抛出异常。如果希望使用使用包含参数的构造器,就要调用Constructor类中的newInstance方法。

利用反射分析类的能力

java.lang.reflect包中的类FieldMethodConstructor用于描述类的域、方法和构造器。Modifier类提供了static方法和常量,可以对类和成员的访问修饰符进行解码。

Class类中的getXXX方法和getDeclaredXXX方法所返回的对象数组有一定的区别:前者返回公有和超类的(如果有超类),后者返回所有的(不包含超类)

注:ConstructorMethod类中的getParameterTypes方法可以返回构造器或方法的参数的类型的Class对象数组。Method类中的getReturnType方法返回方法的返回参数类型的Class对象。

Modifier类中有一系列的static boolean方法用于检测方法的修饰符。getModifiers方法会返回修饰符的int位模式存储,每一位代表一种修饰符。Modifier类还提供了toString方法,返回修饰符位模式对应的字符串表示。

在运行时使用反射分析对象

如果已经获得了确定对象的某个域对象,可以调用Field类中的get(Object obj)方法来获取某个对象中该域的当前值,并存储在相应类的对象中返回。

// harry是一个员工对象,有一个域name
Employee harry = new Employee("Harry Hacker");
// 通过getClass获取harry所属的类的Class对象cl
Class cl = harry.getClass();
// 通过getDeclaredField("name")获取cl中存储的name域的Field对象f
Field f = cl.getDeclaredField("name");
// f调用get(harry)获取harry这个对象的name域的当前值,并存储在Object对象v中
Object v = f.get(harry);
// v的输出结果是Harry Hacker

这里name是私有域,get方法会抛出illegalAccessException。可以调用setAccessible方法覆盖Java的访问控制,使反射机制的默认行为不再受限。

f.setAccessible(true);
使用反射编写泛型数组代码

利用java.lang.reflect包中的Array类可以动态地扩展已经创建的数组。由于一个数组的元素类型在这个数组被创建时就已经被记录了,无法将Object对象数组转换为需要的类对象数组。因此,需要利用Array类中的静态方法newInstance(componentType, newLength)来构造一个所需类的对象数组,这样在之后的扩展过程中,可以将这个数组转换为Object类后再转换为原来的类。扩展操作如下:

public static Object CopyOf(Object a, int newLength)
{
    // 首先获取对象a的Class对象cl
    Class cl = a.getClass();
    // 判断a是否是Array类对象
    if (!cl.isArray()) return null;
    // 确定数组对应的类型
    Class componentType = cl.getComponentType();
    // 获取数组a的长度
    int length = Array.getLength(a);
    // 构造一个和a的类型相同的新Array数组,长度为newLength
    Object newArray = Array.newInstance(componentType, newLength);
    // 将a的内容复制到新的Array数组中(有一种realloc的既视感)
    System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
    return newArray;
}
调用任意方法

利用Method类中的invoke方法可以调用任何对象的方法。invoke的签名如下:

// 第一个参数obj是隐式参数,对于静态方法,被设置为null,后面的显式参数表示被调用的对象数组。
Object invoke(Object obj, Object... args)

由于隐式的参数实际上是Method对象,所以需要调用getMethod来获取想要调用的方法的Method对象。

// 第一个参数是方法名的字符串,后面的显式参数表示方法的参数类型,用来辨别同名不同参数的方法。
Method getMethod(String name, Class... parameterTypes)

建议:仅在必要时使用Method对象,最好使用接口以及lambda表达式。

猜你喜欢

转载自www.cnblogs.com/aries99c/p/12388585.html