给定一个Class实例,我们可以获得Constructor(构造器)、Method(方法)和Field(域),而这些对象提供了“通过程序来访问类的成员变量、域类型、方法签名等信息”的能力。
通过调用Constructor、Method、Field实例上的方法,可以:构造底层类的实例、调用底层类的方法、访问底层类中的域。例如,Method.invoke 使你可以调用任何类的任何对象上的任何方法(遵从常规的安全限制);反射机制甚至能允许一个类使用另一个类,即使当前者被编译时后者还根本不存在(实例化)。
使用反射机制能力,是要付出一些代价的:
下面举个例子:创建一个Set<String> 实例,它的类是由第一个命令行参数指定的,然后将剩余参数插入到集合里面。
public class ReflectClass {
public static void main(String[] args) {
Class<?> c1 = null;
try {
c1 = Class.forName(args[0]);
} catch (ClassNotFoundException e) {
System.err.println("Class not found.");
System.exit(1);
}
Set<String> s = null;
try {
s = (Set<String>) c1.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
s.addAll(Arrays.asList(args).subList(1, args.length));
System.out.println(s);
}
}
输出结果:
[1,2,3,45]
这个程序像一个“玩偶”,但它演示的这种方法时很强大的:
反射机制是一种功能强大的机制,对于特定的复杂系统编程任务,它是非常必要的,但也有一些缺点。如果你编写的程序必须要与编译时未知的类一起工作,如有可能,就应该仅仅使用反射机制来实例化对象,而访问对象时则使用编译时已知的某个接口或超类。