1、简介
反射库(reflection library)提供了一个非常丰富的工具集,以便编写能够动态的操纵java代码中的程序。这项功能被大量应用于javaBeans中,他是java组件的体系结构。特别是在设计或运行中添加新类时,能够快速应用开发工具动态地查询新添加类的能力。能够分析类能力的程序被称为反射(reflective)。
反射机制功能:
- 在运行时分析类的能力。
- 在运行时查看对象,例如,编写一个toString方法供所有类使用。
- 实现数组的操作代码。
- 利用Method对象,这个对象很象C++中的函数指针。
2、Class类
在程序运行期间,java运行时系统始终为所有的对象维护被一个称为运行时的类型标识。这个信息保存着每个对象所属的类的足迹。虚拟机利用运行时信息选择相应的方法执行。
然而可以通过专门的java类访问这些信息。保存这些信息的类被称为Class。Object的getClass方法会返回一个Class的实例。
如同一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定的类的属性。最常用的Class方法是getName。这个方法返回的就是类的名字。例如下面这条语句:
System.out.println(e.getClass().getName()+" "+e.getName())
//如果e是一个雇员 打印结果为:Employee Harry Hacker
//如果e是一个经理 打印结果为:Manager Harry Hacker
如果类在一个包里,包的名字将作为类名的一部分:
Date d=new Date();
Class c1=d.getClass();
String name=c1.getName();//java.util.Date
//调用静态方法 forName
String className="java.util.Date";
Class clazz=Class.forName(className);
如果类名保存在字符串中,并且在运行中可改变,就可以使用这个方法。只有在class是类名或接口名时才能执行。否则,forName将抛出一个checkedexception(已检查异常)。使用该方法,应该提供一个异常处理器。
2.1 java.lang.Class
static Class forName(String className);
返回描述为className的Class对象
Object newInstance();
返回该类的一个实例
2.2 java.lang.reflect.Constructor
Object newInstance(Object[] args);
构造一个这个构造其所属类的新实例
参数:args 这是提供给构造器的参数。
3、利用反射分析类的能力
在java.lang.reflect包中有三个类Field,Method 和Construtor分别用于描述类的域,方法和构造器。这三个类都有getName方法用于返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的对象。Method和Constructor类有能够报告参数类型的方法。这三个类还有一个叫做getModifiers方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用的状况。另外,还可以用Modifier类中的静态方法来分析getModifiers返回的整型数值进行分析,另外还可以利用,Modifier.toString方法将修饰法打印出来。
Class中的getFields、getMethods和getConstructors方法将分别返回类中声明的全部域、方法和构造器、其中包括超类的公有成员。Class的getDeclareFields,getDeclareFieldsMethods和getDeclareFieldsConstructors方法将放回类中声明的全部域,方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。
下面这个例子将打印出类的全部信息:
public static void getAllMember(String ClassName) throws ClassNotFoundException {
Class clazz=Class.forName(ClassName);
System.out.println(Modifier.toString(clazz.getModifiers())+" "+clazz.getName()+" extends "+clazz.getSuperclass().getName()+"{");
for(Field i:clazz.getDeclaredFields()){
System.out.println(Modifier.toString(i.getModifiers())+" "+i.getType().getName()+" "+i.getName()+";");
}
for (Constructor i:clazz.getConstructors()){
System.out.print(Modifier.toString(i.getModifiers())+" "+i.getName()+"(");
int count=0;
for(Class c:i.getParameterTypes()){
System.out.print(c.getName());
count++;
if(count!=i.getParameterCount()){
System.out.print(",");
}
}
System.out.print("){};");
System.out.println();
}
for(Method i:clazz.getDeclaredMethods()){
System.out.print(Modifier.toString(i.getModifiers())+" "+i.getReturnType().getName()+" "+i.getName()+"(");
int count=0;
for(Class c:i.getParameterTypes()){
System.out.print(c.getName());
count++;
if(count!=i.getParameterCount()){
System.out.print(",");
}
}
System.out.print("){};");
System.out.println();
}
System.out.println("}");
}
}
下面是常用的方法介绍:
- Field[] getFields()
- Field[] getDeclaredFields()
getFields方法返回了一个Field对象的数组,这些对象记录了这个类或其超类的公有方域。getDeclareField方法也将返回包含Fields对象的数组,这些对象记录了这个类的全部域。如果类中没有没有域,或者Class对象描述的是基本数据类型或者数组类型,这些方法返回长度为0的数组。
- Method[] getMethods()
- Method[] getDeclareMethods()
返回包含Method对象的数组:getMethods将返回所有的公共方法,包含从超类继承的公有方法;getDeclaredMethods返回这个类或接口的全部方法,但不有超类继承的方法。
- Constructor[] getConstructors()
- Constructor[] getDeclaredConstructors()
返回包含Constructor对象的数组,其中包含了Class对象所描述的类的所有公有构造器或所有构造器。
- Class getDeclaringClass()
返回一个用于描述类中定义的构造器、方法或域的修饰符的整型数值。使用Modifier类中的这个方法可以分析这个值。
- Class[] getExceptionTypes()(在Constructor和Method类中)
返回一个用于描述方法抛出的异常类型的class对象数组。
- int getModifiers()
返回一个用于描述构造器、方法或域的修饰符的整型数值。使用Modifier类中的这个方法可以分析这个返回值。
- String getName()
返回一个用于描述构造器、方法或域名的字符串。
- Class[] getParameterTypes()(在Constructor和Method类中)
返回一个用于描述参数类型的Class对象数组。
- Class getReturnType()(在Method类中)
返回一个描述返回类型的Class对象。
- static String toString()(在Method类中)
返回对应的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 isStrict(int modifiers)
- static boolean isSynchonizedt(int modifiers)
- static boolean isVolatile(int modifiers)
这些方法将检测方法名中对应的修饰符在modifiers值中的位。
4、在运行时使用反射分析对象
在编写程序时,如果知道想查看的域名和类型,查看指定的域是一件很容易的事。而利用反射机制可以查看在编译时还不清楚的对象域。
查看对象域的关键方法是Field类中get方法。如果f是一个Field类型对象(例如,通过getDeclareFields得到的对象),obj是某个包含f域的对象,f.get(obj)将返回一个对象,其值为obj域的当前值。
Employee harry = new Employee("Harry Hacker",35000,10,1,1989);
Class c1=harry.getClass();
Field f=c1.getDeclareField("name");
Object v=f.get(harry);//v就是Harry Hacker
实际上,上面这段代码存在一个问题。由于name是一个是一个私有域,所以get方法将会抛出一个异常IllegalAccessException。只有利用get方法才能得到可访问域的值。除非拥有访问权限,否则java安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。
反射机制的默认行为受限于java的访问控制。然而,如果一个java程序没有受到安全管理器的控制,就可以覆盖访问控制。为了达到这个目的,就需要调用Field,Method和Constructor对象的setAccessible方法。
setAccessible方法是AccessibleObject类中的一个方法,他是Field,Method和Constructor对象的公共超类。这个特性是为调试、持久储存和相似机制提供的。
get方法还有一个需解决的问题。name是String类型,因此把它当作Object类型返回没什么问题。但是,假定我们想查看salary域。它属于double类型,而java数值类型不是对象。要想解决这个问题,可以使用Field类中的getDouble,也可以调用get方法,此时反射机制将会自动将这个域打包到相应的对象包装器中,这里将打包成Double。
当然,可以获得就可以设置。调用f.set(obj,value)可以将obj对象的f域值设置成新值。
下面写一个可供任意类使用的通用toString方法。其中使用getDeclaredFields获得所有数据域,然后使用setAcessible将所有域设成可访问的。对于每个域,获得了名字和值。
public class ObjectAnalyzer {
ArrayList visited;
{
visited=new ArrayList();
}
public String toString(Object o){
if(o==null) return "null";
if(visited.contains(o)) return "...";
visited.add(o);
Class clazz=o.getClass();
if(clazz==String.class){
return (String)o;
}
if(clazz.isArray()){
String r=clazz.getComponentType()+"[]{";
for(int i=0;i< Array.getLength(o);i++){
if(i>0) r+=",";
Object val=Array.get(o, i);
if(clazz.getComponentType().isPrimitive()) r+=val;
else r+=toString(val);
}
return r+="}";
}
String r=clazz.getName();
do{
r+="[";
Field[] fields=clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields,true);
for(Field f:fields){
if(!Modifier.isStatic(f.getModifiers())){
if(!r.endsWith("[")){
r+=",";
}
r+=f.getName()+"=";
try{
Class t=f.getType();
Object val=f.get(o);
if(t.isPrimitive()) r+=val;
else r+=toString(val);
}catch (Exception e){
e.printStackTrace();
}
}
r+="]";
clazz=clazz.getSuperclass();
}
}while(clazz!=null);
return r;
}
}