java反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵java代码的程序。这项功能被大量应用于JavaBeans中。
能够分析类能力的程序称为反射。反射机制作用:
1)运行时分析类的能力
2)运行时查看对象
3)实现通用的数组操作代码
4)利用Method对象,这个对象类似于c++函数指针。
Class类
程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应方法执行。
常用相关方法:
static Class forName(String className) //返回描述类名为className的Class对象
static String getClass(Class classString) //返回描述类名的String
Object newInstance() //返回这个类的新实例
Object newInstance(Object[] args) //构造一个这个构造器所属类的新实例,参数由args给出
利用反射分析类
反射机制最重要的内容--检查类的结构
Class类中的相关方法:
Field[] getFields() //返回一个包含Field对象的数组,记录了这个类与其超类的公有域
Field[] getDeclaredFields() //返回一个包含Field对象的数组,记录了这个类的全部域
Method[] getMethods() //返回一个包含Method对象的数组,记录了所有的公有方法
Method[] get DeclareMethods() //返回一个包含Method对象的数组,记录了这个类所有或接口的所有方法
Constructor[] getConstructors() //返回一个包含Constructor对象的数组,记录了所有公有构造器
Constructor[] getDeclareConstructors() //返回一个包含Constructor对象的数组,记录了所有构造器
反射包中相关方法:
Class getDeclareClass() //返回一个用于描述类中定义的构造器,方法或域的对象
Class getExceptionTypes() //返回一个用于描述方法抛出异常类型的Class对象数组
int getModefiers() //返回一个用于描述构造器,方法或域的修饰符的整形数值。使用Modefier类中的这个方法可以分析这个值
String getName() //返回一个用于描述构造器,方法或域名的字符串
class[] getParrameterrTypes() //返回一个用于描述参数类型的Class对象数组
Class getReturnType() //返回一个用于描述返回类型的Class对象
static String toString(int modifiers) //返回对应modefiers中位设置的修饰符的字符串表示
static boolean isAbatract【或isFinal/isInterface.....所有修饰符】 (int modefiers) //检测修饰符对应位
以下是一个测试用例:
输入一个类,打印所有构造器,方法和域,包括他们的修饰符以及构造器和方法的参数
1 packagerefleection.company;2
3 import java.lang.reflect.*;4 importjava.util.Scanner;5
6 public classReflectionTest {7
8 public static voidmain(String[] args) {9 String name;10 if (args.length > 0) {11 name = args[0];12 } else{13 Scanner in = newScanner(System.in);14 System.out.println("Enter class name(e.g. java.util.Date):");15 name =in.next();16 }17
18 try{19 Class c1 = Class.forName(name);//获取对应类名的Class对象
20 Class superc1 = c1.getSuperclass();//获取超类对象
21 String modifiers = Modifier.toString(c1.getModifiers());//获取修饰符
22 if (modifiers.length() > 0) {23 System.out.print(modifiers + " ");24 }25 System.out.print("class " +name);26 if (superc1 != null && superc1 != Object.class) {27 //打印超类对象
28 System.out.print("extends" +superc1.getName());29 }30 System.out.print("\n{\n");31 printConstructors(c1);32 System.out.println();33 printMethods(c1);34 System.out.println();35 printFields(c1);36 System.out.println("}");37 } catch(ClassNotFoundException e) {38 e.printStackTrace();39 }40 System.exit(0);41 }42
43
44 public static voidprintConstructors(Class c1) {45 Constructor[] constructors = c1.getDeclaredConstructors();//获取所有构造器,存于数组
46
47 for(Constructor c : constructors) {48 String name =c.getName();49 System.out.print(" ");50 String modifiers = Modifier.toString(c.getModifiers());//getModifiers返回一个描述修饰符的整数值,转为字符串
51 if (modifiers.length() > 0) {52 System.out.print(modifiers + " ");53 }54 System.out.print(name + "(");55 Class[] paramTypes = c.getParameterTypes();//获取描述参数类型的对象数组
56 for (int j = 0; j < paramTypes.length; j++) {57 if (j > 0) {58 System.out.print(", ");59 }60 System.out.print(paramTypes[j].getName());61 }62 System.out.print(");");63 }64 }65
66 public static voidprintMethods(Class c1) {67 Method[] methods = c1.getDeclaredMethods();//获取所有方法,存于数组
68
69 for(Method m : methods) {70 Class retType = m.getReturnType();//获取描述返回类型的对象
71 String name =m.getName();72 System.out.print(" ");73 String modifiers = Modifier.toString(m.getModifiers());//描述修饰符的字符串
74 if (modifiers.length() > 0) {75 System.out.print(modifiers + " ");76 }77 System.out.print(retType.getName() + " " + name + "(");78 Class[] paramTypes = m.getParameterTypes();//获取描述参数类型的对象数组
79 for (int j = 0; j < paramTypes.length; j++) {80 if (j > 0) {81 System.out.print(", ");82 }83 System.out.print(paramTypes[j].getName());84 }85 System.out.println(");");86 }87 }88
89 public static voidprintFields(Class c1) {90 Field[] fields =c1.getDeclaredFields();91
92 for(Field f : fields) {93 Class type = f.getType();//域所属类型
94 String name =f.getName();95 System.out.print(" ");96 String modifiers = Modifier.toString(f.getModifiers());//域修饰符
97 if (modifiers.length() > 0) {98 System.out.print(modifiers + " ");99 }100 System.out.println(type.getName() + " " + name + ";");101 }102 }103 }
View Code
输入java.util.Date得到下面结果
1 Enter classname(e.g. java.util.Date):2 java.util.Date3 public classjava.util.Date4 {5 public java.util.Date(int, int, int, int, int, int); public java.util.Date(); public java.util.Date(int, int, int, int, int); public java.util.Date(java.lang.String); public java.util.Date(long); public java.util.Date(int, int, int);6 public void setYear(int);7 public intgetMonth();8 public void setMonth(int);9 public void setDate(int);10 public intgetDay();11 public intgetHours();12 public void setHours(int);13 public intgetMinutes();14 public void setMinutes(int);15 public intgetSeconds();16 public void setSeconds(int);17 private final longgetTimeImpl();18 static final longgetMillisOf(java.util.Date);19 private static finaljava.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String);20 publicjava.lang.String toLocaleString();21 publicjava.lang.String toGMTString();22 public intgetTimezoneOffset();23 private finalsun.util.calendar.BaseCalendar$Date getCalendarDate();24 private static final sun.util.calendar.BaseCalendar getCalendarSystem(long);25 private static finalsun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date);26 private static final sun.util.calendar.BaseCalendar getCalendarSystem(int);27 private static final synchronizedsun.util.calendar.BaseCalendar getJulianCalendar();28 publicjava.time.Instant toInstant();29 public static long UTC(int, int, int, int, int, int);30 public intgetYear();31 public booleanbefore(java.util.Date);32 public booleanafter(java.util.Date);33 public void setTime(long);34 public longgetTime();35 public intgetDate();36 public booleanequals(java.lang.Object);37 publicjava.lang.String toString();38 public inthashCode();39 publicjava.lang.Object clone();40 public intcompareTo(java.util.Date);41 public volatile intcompareTo(java.lang.Object);42 public staticjava.util.Date from(java.time.Instant);43 private voidreadObject(java.io.ObjectInputStream);44 private voidwriteObject(java.io.ObjectOutputStream);45 private finalsun.util.calendar.BaseCalendar$Date normalize();46 private finalsun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date);47 public static longparse(java.lang.String);48
49 private static finalsun.util.calendar.BaseCalendar gcal;50 private staticsun.util.calendar.BaseCalendar jcal;51 private transient longfastTime;52 private transientsun.util.calendar.BaseCalendar$Date cdate;53 private static intdefaultCenturyStart;54 private static final longserialVersionUID;55 private static final[Ljava.lang.String; wtb;56 private static final[I ttb;57 }
输出结果
在运行时使用反射分析对象
有时候希望可以看到数据域的实际数据而不只是类型,用反射同样可以做到。可能用到下列函数
Object get(Object obj) //返回obj对象中用Field对象表示的域值
void set(Object obj,Object newValue) //用一个新值设置obj对象中Field对象表示的域
Field getField(String) //获得指定名称的公有域
Field getDeclareField(String) //获得给定名称的域
假设有一个Employee的类,该类有一个name私有域,根据以上函数,尝试获取域值
Employee e = newEmployee();
Class c1=e.getClass();
Field f= c1.getDeclareField("name");
Object v= f.get(e)
看似可行,实际上时不可行的,它会抛出一个IllegalAccessException异常。name是私有域,java安全机制不允许没有访问权限的客户读取私有域的值,那么就要用到以下函数
void setAccessible(boolean flag) //为反射对象设置可访问标志
boolean isAccessible() //返回反射对象的可访问标志的值
static void setAccessible(AccessibleObject[] array,boolean flag) //设置对象数组可访问标志
那么上述例子增加一条语句,就不会抛出异常了。
f.setAccessable(true);
以下是一个实例,输入任意类,输出它的所有域以及相应的值
1 packageobjectAnalyzer;2
3 importjava.lang.reflect.AccessibleObject;4 importjava.lang.reflect.Array;5 importjava.lang.reflect.Field;6 importjava.lang.reflect.Modifier;7 importjava.util.ArrayList;8
9 public classObjectAnalyzer {10 private ArrayList visited = new ArrayList<>();11
12 publicString toString(Object obj){13 if (obj==null){14 return "null";15 }16 if(visited.contains(obj)){17 return "...";18 }19 visited.add(obj);20 Class c1 = obj.getClass();//获取类名
21 if (c1 == String.class){22 return(String)obj;23 }//若为String类型,直接返回String对象
24 if(c1.isArray()){25 String r = c1.getComponentType()+"[]{";//getComponent返回表示数组元素类型的字符串
26 for (int i = 0;i < Array.getLength(obj);i++){27 if (i>0){28 r += ",";29 }//获取每个数值
30 Object val =Array.get(obj,i);31 if (c1.getComponentType().isPrimitive()){//判断是否为基本类型
32 r +=val;33 }34 else{35 r +=toString(val);36 }37 }38 return r + "}";39 }40 String r = c1.getName();//非空,非String,非array,其他类
41 do{42 r += "[";43 Field[] fields = c1.getDeclaredFields();//获取所有域
44 AccessibleObject.setAccessible(fields,true);//设置为可见
45 for(Field f:fields){46 if (!Modifier.isStatic(f.getModifiers())){//非静态域
47 if(!r.endsWith("[")){48 r += ",";49 }50 r += f.getName() + "=";//获取名称
51 try{52 Class t =f.getType();53 Object val =f.get(obj);54 if (t.isPrimitive()){//判断是否为基本类型
55 r +=val;56 }57 else{58 r +=toString(val);59 }60 }catch(Exception e){61 e.printStackTrace();62 }63 }64 }65 r += "]";66 c1 =c1.getSuperclass();67 } while (c1 != null);68 returnr;69 }70 }
View Code
1 packageobjectAnalyzer;2
3 importjava.util.ArrayList;4
5 public classObjectAnalyzerTest {6 public static voidmain(String[] args){7 ArrayList squares = new ArrayList<>();8 for (int i = 1;i <= 5;i++){9 squares.add(i*i);10 }11 System.out.println(newObjectAnalyzer().toString(squares));12 }13
14 }
测试类
结果
1 java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]
View Code
使用反射编写泛型数组代码
java反射包中的Array类允许动态地创建数组,将其拓展为更通用的方法,即可以拓展任意类型的数组。
public static Object goodCopyOf(Object a,intlength){
Class c1=a.getClass();if(!c1.isarray()){return null;
}
Class componentType=c1.getComponentType();int length =Array.getLength(a);
Object newArray=Array.newInstance(ComponentType, newLength);
System.arraycopy(a,0,newArray,0,Math.min(length,newLength));returnnewArray;
}
Array类的一些方法
static Object get(Object array,int index) //返回给定数组下标的内容
static xxx getXxx(Object array,int index) //返回给定数组下标的内容,xxx是基本类型
static void set(Object array,int index,Object newValue) //设置指定下标内容
staticc int getLength(Object array) //返回数组长度
static Object newInstance(Class componentType,int length) //返回一个具有给定类型,维数的新数组
static Object newInstance(Class componentType,int[] length)
调用任意方法
Methods类中有一个invoke方法,允许调用包装在当前Method对象中的方法。如下:
public Object invoke(Object implicitParameter,Object[] explicitParamenters) //调用这个对象描述的方法,传递给定参数,返回方法的返回值。对于静态方法,把null作为隐式参数传递。