我们知道一个对象在运行时有两种类型,一个是编译类型,一个是运行时类型。在程序运行时,往往是需要发现类和对象的真实的信息的。那么如何获的这种信息呢?
其一,如果我们在编译和运行时都知道类型的具体信息,这时是可以手动将一个对象转换为运行时的类型。
其二,如果我们在编译时无法预知对象和类到底是属于哪些类,那么程序只有依靠运行时的信息来发现对象和类的真实的信息了,这时就必须要用到反射技术。
在谈具体的发射技术之前,我想先回顾下,有关类的加载的一些基本的性质和原理,以方便我们更好地理解,反射的作用和特点。而实际上,一个类如果可以被执行,那么对于JVM来说,它的执行流程为:类的加载、连接、初始化。通过这种方式,才可以获取到一个类的类对象,即java.lang.Class对象,并在此基础上获取到该类的成员变量,方法和构造器等内在的东东。
那么,什么是类的加载呢?类的加载就是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序使用任何类时,系统都会为之建立一个java.lang.Class对象。同时,类的加载是由类加载器完成的。
类的连接:连接阶段负责把类的二进制数据合并到JRE中。第一,验证,检验被加载的类是否有正确的内部结构;第二,准备,负责为类的类变量分配内存,并设置默认初始值。第三,解析将类的二进制数据中的符号引用替换成直接引用。类的初始化,主要对类变量进行初始化。
这样,我们清楚地认识到了一个类的生命周期变化,那么这些也为我们的反射机制带来了铺垫,要获取一个类的class对象,有三重方式:
(1)使用Class类的forName(String clazzName)静态方法
(2)调用某个类的class属性获取该类对应的Class对象
(3)调用某个类的getClass()方法
通过这三种手段就可以获取到了一个类的Class对象,这样就可以动态获取该对象的实际的信息了。下面通过一个具体的例子说明下,如果通过反射机制,创建一个类的对象,如何通过获取的对象再进一步获取该对象的属性和方法,以及动态为该对象注入新的参数值。
1 package ReflectionEntity; 2 import java.util.Date; 3 4 /** 5 * @author XuJiaqing 6 * 7 */ 8 public class Student { 9 //get/set方法 10 public String getStudentID() { 11 return studentID; 12 } 13 14 public void setStudentID(String studentID) { 15 this.studentID = studentID; 16 } 17 18 public String getStudentName() { 19 return studentName; 20 } 21 22 public void setStudentName(String studentName) { 23 this.studentName = studentName; 24 } 25 26 public Date getBirthday() { 27 return birthday; 28 } 29 30 public void setBirthday(Date birthday) { 31 this.birthday = birthday; 32 } 33 34 public int getScore() { 35 return score; 36 } 37 38 public void setScore(int score) { 39 this.score = score; 40 } 41 42 private String studentID;//学生id 43 public String studentName;//学生姓名 44 private Date birthday;//学生生日 45 private int score;//学生成绩 46 //实现的任务 的方法 47 public void achieveTask(String taskName) { 48 System.out.println(studentName + "实现了" + taskName + "技术"); 49 } 50 }
package ReflectionTest; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import ReflectionEntity.Student; /** * @author XuJiaqing * */ public class test { public static void main(String[] args) { // TODO Auto-generated method stub try { // 获取Student的Class对象 Class<?> clazz = Class.forName("ReflectionEntity.Student"); // 获取该类中所有的属性 Field[] fields = clazz.getDeclaredFields(); // 遍历所有的属性 for (Field field : fields) { // 打印属性信息,包括访问控制修饰符,类型及属性名 System.out.println(field); System.out.println("修饰符:" + Modifier.toString(field.getModifiers())); System.out.println("类型:" + field.getType()); System.out.println("属性名:" + field.getName()); } // 获取该类中的所有方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { // 打印方法签名 System.out.println(method); System.out.println("修饰符:" + Modifier.toString(method.getModifiers())); System.out.println("方法名:" + method.getName()); System.out.println("返回类型:" + method.getReturnType()); // 获取方法的参数对象 Class<?>[] clazzes = method.getParameterTypes(); for (Class<?> class1 : clazzes) { System.out.println("参数类型:" + class1); } } // 通过Class对象创建实例 Student student = (Student) clazz.newInstance(); // 获取属性名为studentName的字段(Field)对象,以便下边重新设置它的值 Field studentName = clazz.getField("studentName"); // 设置studentName的值为”XuJiaqing“ studentName.set(student, "XuJiaqing"); // 通过Class对象获取名为”finishTask“,参数类型为String的方法(Method)对象 Method finishTask = clazz.getMethod("achieveTask", String.class); // 调用achieveTask方法 finishTask.invoke(student, "反射"); } catch (Exception e) { e.printStackTrace(); } } }
包的目录结构和运行结果为: