Java 反射 分析类和对象
@author ixenos
摘要:优化程序启动策略、在运行时使用反射分析类的结构和对象
优化程序启动策略
在启动时,包含main方法的类被加载。它会加载所有它需要的类。这些被加载的类又要加载它们需要的类,以此类推,这是JVM的动态加载机制。
对于一个大型的应用程序,这将消耗很多时间,用户体验不好。此时可以通过反射来优化程序启动策略,要确保包含main方法的类没有显示地引用其他的类。
首先,显式一个启动动画,然后通过调用Class.forName手动加载其他的类,预加载。
在运行时使用反射分析类的结构
在java.lang.reflect包中有三个类Field、Method和Constructor,分别用于描述类的域、方法和构造器。
这三个类都有getName方法返回项目的名称
Field类的getType方法可返回描述域所属类型的Class对象
Method和Constructor类都有报告参数的方法,Method类还有报告返回类型的方法
这三个类还有getModifiers方法返回整数数值,用不同的位开关描述public和static这样的修饰符的使用状况。还可以使用java.lang.reflect.Modifier类的静态方法分析getModifiers返回的整数数值,如isPublic、isFinal、isPrivate
Class类中的getFields、getMethods和getConstructors方法返回类提供的public域、方法和构造器数组,其中包括超类的公有成员
Class类中的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将返回类中声明的所有域、方法和构造器,不包括超类的成员
在运行时使用反射分析对象
从上一节知道如何查看任意对象的数据域名称和类型:获得对应的Class对象,通过Class对象调用getDeclareFields
本节进一步查看数据域的实际内容(对对象进行分析)
1.查看数据域的关键方法是Field类中的get方法
如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,其值为obj域的当前值
Employee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989); //反射分析类 Class c1 = harry..getClass(); Field f = c1.getDeclaredField("name"); //反射分析对象 Object v = f.get(harry);
*反射机制的默认行为受限于Java的访问控,然而,如果一个Java程序没有收到安全管理器的控制,就可以覆盖访问控制
*这需要调用Field、Method和Constructor对象的setAccessible方法,如f.setAccessible(true);
*setAccessible是AccessibleObject类中的一个方法,是Field、Method和Constructor类的公共超类
//以下是一个利用反射实现的对任意类型对象的toString方法
1 Class ObjectAnalyzer 2 { 3 public String toString(Object obj) 4 { 5 Class c1 = obj.getClass(); 6 ... 7 String r = c1.getName(); 8 //检查这个类及其所有超类的域 9 do 10 { 11 r += "["; 12 Fields[] fields = c1.getDeclaredFields(); 13 //AccessibleObject.setAccessible也有静态方法哟 14 //使所有的域可以访问 15 AccessibleObject.setAccessible(fields, true); 16 //获得所有域的变量名和值 17 for(Field f : fields) 18 { 19 //静态域在运行时不变,没有分析的必要 20 if(!Modifier.isStatic(f.getModifiers())) 21 { 22 //表明当前类是上一次分析的类 23 if(!r.endsWith("[")) { r +=" ,"; } 24 //变量名 = ... 25 r += f.getName() + "="; 26 try 27 { 28 //get当前obj对象的f域的值 29 Object val = f.get(obj); 30 r += toString(val); 31 } 32 catch(Exception e) { e.printStackTrace(); } 33 } 34 } 35 //完成当前类,前进到基类 36 r += "]"; 37 c1 = c1.getSuperClass(); 38 } 39 //当没有基类时结束循环 40 while (c1 != null); 41 return r; 42 } 43 ... 44 }
//使用该toSting方法示例:
public String toString(){ return new ObjectAnalyzer().toString(this); } //使用this指向当前对象
1 import java.util.ArrayList; 2 3 /** 4 *这个程序使用反射的toString方法来查看任意对象的内部信息 5 */ 6 7 public class ObjectAnalyzerTest 8 { 9 public class void main(String[] args) 10 { 11 ArrayList<Integer> squares = new ArrayList<>(); 12 for(int i = 1; i <= 5; i++) 13 { 14 square.add(i * i); 15 } 16 System.out.println(new ObjectAnalyzer().toString(squares)); 17 } 18 }
***This和super都不能在main()方法中使用,main()方法是静态的,this是本类对象的引用,静态先于对象,所以是不能使用的。
为什么?因为静态方法是在类加载的时候执行.类刚加载可能还没有创建对象,所以就不能用this
动态加载请看另一篇文章http://www.cnblogs.com/ixenos/p/5682088.html
//对toString改进使其能查看数组内部,以及记录被访问过的对象来阻止循环引用
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 { 11 private ArrayList<Object> visited = new ArrayList<>(); 12 13 /** 14 * Converts an object to a string representation that lists all fields. 15 * @param obj an object 16 * @return a string with the object's class name and all field names and 17 * values 18 */ 19 public String toString(Object obj) 20 { 21 if (obj == null) return "null"; 22 if (visited.contains(obj)) return "..."; 23 visited.add(obj); 24 Class cl = obj.getClass(); 25 if (cl == String.class) return (String) obj; 26 if (cl.isArray()) 27 { 28 String r = cl.getComponentType() + "[]{"; 29 for (int i = 0; i < Array.getLength(obj); i++) 30 { 31 if (i > 0) r += ","; 32 Object val = Array.get(obj, i); 33 if (cl.getComponentType().isPrimitive()) r += val; 34 else r += toString(val); 35 } 36 return r + "}"; 37 } 38 39 String r = cl.getName(); 40 // inspect the fields of this class and all superclasses 41 do 42 { 43 r += "["; 44 Field[] fields = cl.getDeclaredFields(); 45 AccessibleObject.setAccessible(fields, true); 46 // get the names and values of all fields 47 for (Field f : fields) 48 { 49 if (!Modifier.isStatic(f.getModifiers())) 50 { 51 if (!r.endsWith("[")) r += ","; 52 r += f.getName() + "="; 53 try 54 { 55 Class t = f.getType(); 56 Object val = f.get(obj); 57 if (t.isPrimitive()) r += val; 58 else r += toString(val); 59 } 60 catch (Exception e) 61 { 62 e.printStackTrace(); 63 } 64 } 65 } 66 r += "]"; 67 cl = cl.getSuperclass(); 68 } 69 while (cl != null); 70 71 return r; 72 } 73 }
虚拟机为每个类型管理一个Class对象,因此可以用==实现两个Class对象的比较:if(e.getClass()==Employee.class)
2.设置数据域的关键方法是Field类中的set方法
调用f.set(obj, value)可以将obj对象的f域设置成新值