能够分析类能力的程序称为 反射。反射机制的功能有:
- 在运行时分析类的能力。
- 在运行时检查对象。例如,编写一个适用于所有类的
toString
方法。 - 实现泛型数组操作代码。
- 利用
Method
对象。
1 Class类
在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识,这些信息会跟踪每个对象所属的类,虚拟机利用运行时类型信息选择要执行的正确的方法。Class
类用于保存这些信息。
Object
类中的getClass
方法返回一个Class
类型的实例,Class
类中的getName
方法返回类名,这两个方法结合起来可以用来获得类名。如果类在一个包里,包名也会作为类名的一部分。例如:
Employee e;
System.out.println(e.getClass().getName());
Class中的静态方法forName
可以获得类名对应的Class对象,它的签名和用法如下:
/* 签名 */
static Class forName(String className)
/* 用法 */
String className = "java.util.Random";
Class cl = Class.forName(className);
如果类名保存在一个字符串中,这个字符串会在运行时变化,就可以使用这个方法在运行时获得不同的Class
对象。如果className
是一个类名或接口名,这个方法可以正常执行;否则,将抛出一个检查型异常(checked exception)。无论何时使用这个方法,都应该提供一个异常处理器。
第三种获得Class对象的方法是,如果T
是任意的Java类型,T.class
将代表匹配的Class
对象。例如:
Class cl1 = Random.class; // import java.util.*
Class cl2 = int.class;
Class cl3 = Double[].class;
虚拟机为每个类型管理一个唯一的Class
对象,因此,可以利用==
运算符实现两个Class
对象的比较。例如:
if (e.getClass() == Employee.getClass()) ...
如果有一个Class
对象,可以用它构造类的实例。调用getConstructor
方法将得到一个Constructor
类型的对象,然后使用newInstance
方法来构造一个实例。例如:
String className = "java.util.Random";
Class cl = Class.forName(className);
Object obj = cl.getConstructor().newInstance();
这两个方法的签名为:
/* java.lang.Class */
Constructor getConstructor(Class... parameterTypes)
// 生成一个对象,描述有指定参数类型的构造器。如果这个类没有参数匹配的构造器,会抛出异常
/* java.lang.reflect.Constructor */
Object newInstance(Object... params)
// 将 params 传递到构造器,来构造这个构造器声明类的一个新实例
2 声明异常入门
上面提到forName
方法可能抛出异常,调用这个方法时要提供异常处理器。下面简要介绍一些异常处理的内容。
当运行时发生错误时,程序就会抛出一个异常,可以提供一个处理器捕获这个异常并进行处理。如果没有提供处理器,程序就会终止,并在控制台上打印出一个消息,给出异常的类型。
异常有两种类型:非检查型异常和检查型异常。对于检查型异常,这类异常难以避免,编译器将会检查这类异常,在编译阶段提示程序员解决问题。非检查型异常不检查,在编译阶段不报错,一般可以避免。例如,数组越界和访问null
引用属于非检查型异常。
如果一个方法包含可能抛出检查型异常的语句,则在方法名上添加一个throws
子句,调用这个方法的任何方法也都需要一个throws
声明。例如:
public static void doSomethingWithClass(String name)
throws ReflectiveOperationException
{
Class cl = Class.forName(name); // 可能抛出异常
// do something with cl
}
如果调用了一个可能抛出检查型异常的方法而没有提供相应的异常处理器,编译器就会报错。
3 利用反射分析类的能力
在java.lang.reflect
包中有三个类Field
、Method
和Constructor
,分别用于描述类的字段、方法和构造器。这三个类都有getName
方法,用来返回字段、方法或构造器的名称:
String getName()
Field
类有一个getType
方法,用来返回描述字段类型的一个Class
对象:
Class getType()
Method
和Constructor
类中的getParameterTypes
方法返回参数类型:
Class[] getParameterTypes()
Method
类中的getReturnType
方法得到返回类型:
Class getReturnType()
这三个类都有一个getModifier
方法,它返回一个整数,描述这个构造器、方法或字段的修饰符。可以使用Modifier
类中的方法来分析这个整数值。
/* java.lang.reflect.Field/Method/Constructor */
int getModifier()
/* java.lang.reflect.Modifier */
static String toString(int modifiers)
// 返回一个字符串,包含 modifiers 中对应的修饰符
static boolean isAbstract(int modifiers)
static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isProtected(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchronized(int modifiers)
static boolean isVolatile(int modifiers)
// 判断整数是否包含对应的修饰符
Class
类中提供了一些返回Field
、Method
和Constructor
对象的方法:
/* java.lang.Class */
Field getField(String name) // 得到指定名称的公共字段
Field[] getFields() // 返回这个类及其超类的公共字段
Field getDeclaredField(String name) // 得到指定名称的字段
Field[] getDeclaredFields() // 返回这个类的所有字段,不包括超类的字段
Method getMethod(String name, Class... parameterTypes) // 返回这个类中指定名称和参数类型的公共方法
Method[] getMethods() // 返回所有的公共方法,包括从超类继承来的公共方法
Method getDeclaredMethod(String name, Class... parameterTypes) // 返回这个类中指定名称和参数类型的方法
Method[] getDeclared