Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。
一.Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。保存这些信息的类被称为Class。当我们使用Object类中的getClass方法我们将会得到一个Class类型的实例。
例如,我们可以使用Class类中的getName方法来返回类的名字,也可以通过调用Class类的静态方法Class.forName()方法来获得类名对应的Class对象。同时我们还可以直接使用类名.class的方法来获得一个Class对象。我们还可以使用newInstance()方法来快速创建一个类的实例。
二.利用反射分析类的能力
在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的方法,用来放回项目的名称。这三个类中都有一个getModifiers的方法,它将返回一个整型值,用不同的位开关描述public 和 static 这样的修饰符的使用情况。我们还可以利Modifier.to-String方法将修饰符打印出来。Class类中的getField、getMethod、getConstruc-tor方法分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类中的get-DeclareField、getDeclareMethod、getDeclareConstructor方法分别返回类中的私有方法和受保护成员,但是不包括超类的成员。
下面是Java核心技术5.7节反射的实例代码:
package chapter5;
import java.lang.reflect.*;
import java.util.*;
public class ReflectionTest {
public static void main(String args[]){
Scanner input = new Scanner(System.in);
System.out.print("请输入完整的类名;");
String name = input.next();
try{
Class cl = Class.forName(name);//得到name的Class对象
Class supercl = cl.getSuperclass();//得到Class对象实例的父类的Class对象
String modifiers = Modifier.toString(cl.getModifiers());
if(modifiers.length()>0) System.out.print(modifiers + " ");//判断是否有类修饰词
System.out.print("class " + name);
if(supercl != null &&supercl != Objects.class ) System.out.print(" extends " + supercl.getName());
//判断该类的父类是否为空或Object
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
System.exit(0);
}
public static void printConstructors(Class cl){
Constructor[] constructors = cl.getConstructors();//得到全部的构造器数组
for(Constructor c:constructors){
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());//得到构造器的修饰符
if(modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "(");
Class[] paramTypes = c.getParameterTypes();//得到构造器的方法参数
for(int j = 0;j<paramTypes.length;j++){
if(j > 0) System.out.print(",");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printMethods(Class cl){
Method[] methods = cl.getDeclaredMethods();
for(Method m:methods){
String name = m.getName();
System.out.print(" ");
String modifiers = Modifier.toString(m.getModifiers());
if(modifiers.length() > 0) System.out.print(modifiers + " ");
Class returnType = m.getReturnType();//得到返回值的Class类型
System.out.print(returnType.getName() + " " + name + "(");
Class[] paramTypes = m.getParameterTypes();
for(int j = 0;j<paramTypes.length;j++){
if(j > 0) System.out.print(",");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printFields(Class cl){
Field[] fields = cl.getDeclaredFields();
for(Field f:fields){
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if(modifiers.length() > 0) System.out.print(modifiers + " ");
Class type = f.getType();//得到f的类型
System.out.println(type.getName() + " " + name + ";");
}
}
}
使用java.lang.reflect.Field得到的测试结果:
请输入完整的类名;java.lang.reflect.Field
public final class java.lang.reflect.Field extends java.lang.reflect.AccessibleObject
{
public java.lang.Object get(java.lang.Object);
public boolean equals(java.lang.Object);
public java.lang.String toString();
public int hashCode();
public int getModifiers();
public boolean getBoolean(java.lang.Object);
public byte getByte(java.lang.Object);
public short getShort(java.lang.Object);
public char getChar(java.lang.Object);
public int getInt(java.lang.Object);
public long getLong(java.lang.Object);
public float getFloat(java.lang.Object);
public double getDouble(java.lang.Object);
public java.lang.String getName();
private synchronized java.util.Map declaredAnnotations();
public java.lang.annotation.Annotation getAnnotation(java.lang.Class);
public [Ljava.lang.annotation.Annotation; getAnnotationsByType(java.lang.Class);
public [Ljava.lang.annotation.Annotation; getDeclaredAnnotations();
public java.lang.Class getDeclaringClass();
private sun.reflect.generics.factory.GenericsFactory getFactory();
private sun.reflect.generics.repository.FieldRepository getGenericInfo();
public boolean isSynthetic();
public java.lang.String toGenericString();
private sun.reflect.FieldAccessor acquireFieldAccessor(boolean);
java.lang.reflect.Field copy();
public java.lang.reflect.AnnotatedType getAnnotatedType();
private sun.reflect.FieldAccessor getFieldAccessor(java.lang.Object);
private sun.reflect.FieldAccessor getFieldAccessor(boolean);
private java.lang.String getGenericSignature();
public java.lang.reflect.Type getGenericType();
public java.lang.Class getType();
private native [B getTypeAnnotationBytes0();
public boolean isEnumConstant();
public void set(java.lang.Object,java.lang.Object);
public void setBoolean(java.lang.Object,boolean);
public void setByte(java.lang.Object,byte);
public void setChar(java.lang.Object,char);
public void setDouble(java.lang.Object,double);
private void setFieldAccessor(sun.reflect.FieldAccessor,boolean);
public void setFloat(java.lang.Object,float);
public void setInt(java.lang.Object,int);
public void setLong(java.lang.Object,long);
public void setShort(java.lang.Object,short);
private java.lang.Class clazz;
private int slot;
private java.lang.String name;
private java.lang.Class type;
private int modifiers;
private transient java.lang.String signature;
private transient sun.reflect.generics.repository.FieldRepository genericInfo;
private [B annotations;
private sun.reflect.FieldAccessor fieldAccessor;
private sun.reflect.FieldAccessor overrideFieldAccessor;
private java.lang.reflect.Field root;
private transient java.util.Map declaredAnnotations;
}
三.在运行时使用反射分析方法
我们已经知道如果查看任意对象的数据域名称和类型,那么要怎么进一步查看数据域的实际内容呢?我们依旧可以使用反射机制来实现。
查看对象域的关键方法是Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,那么f.get(obj)将返回一个对象,其值为obj域的当前值。我们依旧来看Java核心技术里面的一个例子:
private String name;
//...
Employee harry = new Employee("Harry Hacker",35000,10,1,1989);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");//name为employee类的一个私有数据域
f.setAccessible(true);//因为name是私有的,需要使用这个方法获得访问权限
Object v = f.get(harry);
通过这段代码我们就能得到Employee类的一个实例harry的数据域name的值“Harray hacker”。
类似的,我们也可以使用f.set(obj,value)将obj对象的f域设置为新值value。
四.使用反射编写泛型数组代码
下面我们通过一个实例来探究这个问题:
在java.util.Arrays类中有一个copyOf方法可以用来拓展已经填满地数组,那么如何来编写这样一个通用地方法呢?
一个思路是用Object[]数组来代替所有数组类型,然后再用类型转换回来。但是很遗憾,这样往往是不可行地。因为Java数组会记住每个元素地类型,即创建数组时new表达式中使用的元素类型。例如,我们可以暂时地将一个Employee[]转换为Object[],再转换回去。但是我们永远不能将一个一开始的Object[]转换为Employee[]。这时我们就需要调用java.lang.reflect包中的Arrays类的一些方法。最常用的还是静态方法newInstance,它能够创建新数组。在调用它时必须提供两个参数,一个是数组的元素类型,一个是数组的长度。
Object newArray = Array.newInstance(componentType,newLength);
下面是Java核心技术里的例程:
public static Object copyOf(Object a,int newLength){
Class cl = a.getClass();
if(!cl.isArray()) return null;
Class componentType = cl.getComponentType();
//通过Class类的getComponentType()方法可以取得一个数组的Class对象
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType,newLength);
System.out.arraycopy(a,0,newArray,Math.min(length,newLength));
return newArray;
}
为了能使这个方法使用与所有数组,而不仅仅是对象数组,应该将上述代码中的a设置为Object类型而不是Object[]。
五.调用任意方法
在大多数情况下,Java是不允许像C/C++那样使用函数指针来执行任意函数。然而,反射机制却允许你调用任意方法。
我们通过Method类中的两个方法来实现它,Method中的invoke方法,它允许调用包装在当前Method对象中的方法。
例如,假设用m1来表示Employee类的getName()方法,我们就可以这么调用它:
String name = (String) m1.invoke(harry);//harry 是一个Employee类的实例
那么我们又要如何得到Method类的对象呢?
当然我们可以通过Method类的getDeclareMethods来逐个查找;
我们也可以通过Class类的getMethod(String className)来实现,当然一个类中可能有几个相同名称的类,这时候就需要考虑它们方法的参数类型,即使用:
Method getMethod(String className,Class … parameterTypes)。