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 package refleection.company; 2 3 import java.lang.reflect.*; 4 import java.util.Scanner; 5 6 public class ReflectionTest { 7 8 public static void main(String[] args) { 9 String name; 10 if (args.length > 0) { 11 name = args[0]; 12 } else { 13 Scanner in = new Scanner(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 void printConstructors(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 void printMethods(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 void printFields(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 }
输入java.util.Date得到下面结果
1 Enter class name(e.g. java.util.Date): 2 java.util.Date 3 public class java.util.Date 4 { 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 int getMonth(); 8 public void setMonth(int); 9 public void setDate(int); 10 public int getDay(); 11 public int getHours(); 12 public void setHours(int); 13 public int getMinutes(); 14 public void setMinutes(int); 15 public int getSeconds(); 16 public void setSeconds(int); 17 private final long getTimeImpl(); 18 static final long getMillisOf(java.util.Date); 19 private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String); 20 public java.lang.String toLocaleString(); 21 public java.lang.String toGMTString(); 22 public int getTimezoneOffset(); 23 private final sun.util.calendar.BaseCalendar$Date getCalendarDate(); 24 private static final sun.util.calendar.BaseCalendar getCalendarSystem(long); 25 private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date); 26 private static final sun.util.calendar.BaseCalendar getCalendarSystem(int); 27 private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar(); 28 public java.time.Instant toInstant(); 29 public static long UTC(int, int, int, int, int, int); 30 public int getYear(); 31 public boolean before(java.util.Date); 32 public boolean after(java.util.Date); 33 public void setTime(long); 34 public long getTime(); 35 public int getDate(); 36 public boolean equals(java.lang.Object); 37 public java.lang.String toString(); 38 public int hashCode(); 39 public java.lang.Object clone(); 40 public int compareTo(java.util.Date); 41 public volatile int compareTo(java.lang.Object); 42 public static java.util.Date from(java.time.Instant); 43 private void readObject(java.io.ObjectInputStream); 44 private void writeObject(java.io.ObjectOutputStream); 45 private final sun.util.calendar.BaseCalendar$Date normalize(); 46 private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date); 47 public static long parse(java.lang.String); 48 49 private static final sun.util.calendar.BaseCalendar gcal; 50 private static sun.util.calendar.BaseCalendar jcal; 51 private transient long fastTime; 52 private transient sun.util.calendar.BaseCalendar$Date cdate; 53 private static int defaultCenturyStart; 54 private static final long serialVersionUID; 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 = new Employee(); 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 package objectAnalyzer; 2 3 import java.lang.reflect.AccessibleObject; 4 import java.lang.reflect.Array; 5 import java.lang.reflect.Field; 6 import java.lang.reflect.Modifier; 7 import java.util.ArrayList; 8 9 public class ObjectAnalyzer { 10 private ArrayList<Object> visited = new ArrayList<>(); 11 12 public String 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 return r; 69 } 70 }
1 package objectAnalyzer; 2 3 import java.util.ArrayList; 4 5 public class ObjectAnalyzerTest { 6 public static void main(String[] args){ 7 ArrayList<Integer> squares = new ArrayList<>(); 8 for (int i = 1;i <= 5;i++){ 9 squares.add(i*i); 10 } 11 System.out.println(new ObjectAnalyzer().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][][]
使用反射编写泛型数组代码
java反射包中的Array类允许动态地创建数组,将其拓展为更通用的方法,即可以拓展任意类型的数组。
public static Object goodCopyOf(Object a,int length){ 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)); return newArray; }
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作为隐式参数传递。