含义:可以获取正在运行的Java对象
Java反射的功能
1)可以判断运行时对象所属的类
2)可以判断运行时对象所具有的成员变量和方法
3)通过反射甚至可以调用到private的方法
4)生成动态代理
1、实现Java反射的类
1)Class:它表示正在运行的Java应用程序中的类和接口
2)Field:提供有关类或接口的属性信息,以及对它的动态访问权限
3)Constructor:提供关于类的单个构造方法的信息以及对它的访问权限
4)Method:提供关于类或接口中某个方法信息
注意:Class类是Java反射中最重要的一个功能类,所有获取对象的信息(包括:方法/属性/构造方法/访问权限)都需要它来实现
2、编写Java反射程序的步骤:
1)必须首先获取一个类的Class对象
例如:
Class c1 = Test.class;
Class c2 = Class.forName(“com.reflection.Test”);
Class c3 = new Test().getClass();
2)然后分别调用Class对象中的方法来获取一个类的属性/方法/构造方法的结构
注意:如果要能够正常的获取类中方法/属性/构造方法应该重点掌握如下的反射类
Field
Constructor
Method
Java反射机制主要提供下面几种用途:
-
在运行时判断任意一个对象所属的类
-
在运行时构造任意一个类的对象
-
在运行时判断任意一个类所具有的成员变量和方法
-
在运行时调用任意一个对象的方法
package com.wanggc.reflection; import java.lang.reflect.Method; /** * Java 反射练习。 * * @author Wanggc */ public class ForNameTest { /** * 入口函数。 * * @param args * 参数 * @throws Exception * 错误信息 */ public static void main(String[] args) throws Exception { // 获得Class Class<?> cls = Class.forName(args[0]); // 通过Class获得所对应对象的方法 Method[] methods = cls.getMethods(); // 输出每个方法名 for (Method method : methods) { System.out.println(method); } } }
这样就列出了java.lang.String类的所有方法名、及其限制符、返回类型及抛出的异常。这个程序使用Class类forName方法载入指定的类,然后调用getMethods方法返回指定类的方法列表。java.lang.reflect.Method用来表述某个类中的单一方法。
使用java的反射机制,一般需要遵循三步:
-
获得你想操作类的Class对象
-
通过第一步获得的Class对象去取得操作类的方法或是属性名
-
操作第二步取得的方法或是属性
Java运行的时候,某个类无论生成多少个对象,他们都会对应同一个Class对象,它表示正在运行程序中的类和接口。如何取得操作类的Class对象,常用的有三种方式:
-
调用Class的静态方法forName,如上例;
-
使用类的.class语法,如:Class<?> cls = String.class;
-
调用对象的getClass方法,如:String str = "abc";Class<?> cls = str .getClass();
1 package com.wanggc.reflection; 2 3 import java.lang.reflect.Method; 4 5 /** 6 * Java 反射练习。 7 * 8 * @author Wanggc 9 */ 10 public class ReflectionTest { 11 public static void main(String[] args) throws Exception { 12 DisPlay disPlay = new DisPlay(); 13 // 获得Class 14 Class<?> cls = disPlay.getClass(); 15 // 通过Class获得DisPlay类的show方法 16 Method method = cls.getMethod("show", String.class); 17 // 调用show方法 18 method.invoke(disPlay, "Wanggc"); 19 } 20 } 21 22 class DisPlay { 23 public void show(String name) { 24 System.out.println("Hello :" + name); 25 } 26 } 前面说过,Java程序的每个类都会有个Class对象与之对应。Java反射的第一步就是获得这个Class对象,如代码14行。当然,每个类的方法也必有一个Method对象与之对应。要通过反射的方式调用这个方法,就要首先获得这个方法的Method对象,如代码16行,然后用Method对象反过来调用这个方法,如代码18行。注意16行getMethod方法的第一个参数是方法名,第二个是此方法的参数类型,如果是多个参数,接着添加参数就可以了,因为getMethod是可变参数方法。执行18行代码的invoke方法,其实也就是执行show方法,注意invoke的第一个参数,是DisPlay类的一个对象,也就是调用DisPlay类哪个对象的show方法,第二个参数是给show方法传递的参数。类型和个数一定要与16行的getMethod方法一直。 上例讲述了如何通过反射调用某个类的方法,下面将再通过一个实例讲述如何通过反射给某个类的属性赋值: 1 package com.wanggc.reflection; 2 3 import java.lang.reflect.Field; 4 5 /** 6 * Java 反射之属性练习。 7 * 8 * @author Wanggc 9 */ 10 public class ReflectionTest { 11 public static void main(String[] args) throws Exception { 12 // 建立学生对象 13 Student student = new Student(); 14 // 为学生对象赋值 15 student.setStuName("Wanggc"); 16 student.setStuAge(24); 17 // 建立拷贝目标对象 18 Student destStudent = new Student(); 19 // 拷贝学生对象 20 copyBean(student, destStudent); 21 // 输出拷贝结果 22 System.out.println(destStudent.getStuName() + ":" 23 + destStudent.getStuAge()); 24 } 25 26 /** 27 * 拷贝学生对象信息。 28 * 29 * @param from 30 * 拷贝源对象 31 * @param dest 32 * 拷贝目标对象 33 * @throws Exception 34 * 例外 35 */ 36 private static void copyBean(Object from, Object dest) throws Exception { 37 // 取得拷贝源对象的Class对象 38 Class<?> fromClass = from.getClass(); 39 // 取得拷贝源对象的属性列表 40 Field[] fromFields = fromClass.getDeclaredFields(); 41 // 取得拷贝目标对象的Class对象 42 Class<?> destClass = dest.getClass(); 43 Field destField = null; 44 for (Field fromField : fromFields) { 45 // 取得拷贝源对象的属性名字 46 String name = fromField.getName(); 47 // 取得拷贝目标对象的相同名称的属性 48 destField = destClass.getDeclaredField(name); 49 // 设置属性的可访问性 50 fromField.setAccessible(true); 51 destField.setAccessible(true); 52 // 将拷贝源对象的属性的值赋给拷贝目标对象相应的属性 53 destField.set(dest, fromField.get(from)); 54 } 55 } 56 } 57 58 /** 59 * 学生类。 60 */ 61 class Student { 62 /** 姓名 */ 63 private String stuName; 64 /** 年龄 */ 65 private int stuAge; 66 67 /** 68 * 获取学生姓名。 69 * 70 * @return 学生姓名 71 */ 72 public String getStuName() { 73 return stuName; 74 } 75 76 /** 77 * 设置学生姓名 78 * 79 * @param stuName 80 * 学生姓名 81 */ 82 public void setStuName(String stuName) { 83 this.stuName = stuName; 84 } 85 86 /** 87 * 获取学生年龄 88 * 89 * @return 学生年龄 90 */ 91 public int getStuAge() { 92 return stuAge; 93 } 94 95 /** 96 * 设置学生年龄 97 * 98 * @param stuAge 99 * 学生年龄 100 */ 101 public void setStuAge(int stuAge) { 102 this.stuAge = stuAge; 103 } 104 }
Java的发射机制中类有Class对应,类的方法有Method对应,当然属性也有Field与之对应。代码中注释已经做了详细的注释,在此不再赘述。但要注意,Field提供了get和set方法获取和设置属性的值,但是由于属性是私有类型,所以需要设置属性的可访问性为true,如代码50~51行。也可以在为整个fields设置可访问性,在40行下面使用AccessibleObject的静态方法setAccessible,如:AccessibleObject.setAccessible(fromFields, true);
前面讲述了如何用Java反射机制操作一个类的方法和属性,下面再通过一个实例讲述如何在运行时创建类的一个对象:
package com.wanggc.reflection; 2 3 import java.lang.reflect.Field; 4 5 /** 6 * Java 反射之属性练习。 7 * 8 * @author Wanggc 9 */ 10 public class ReflectionTest { 11 public static void main(String[] args) throws Exception { 12 // 建立学生对象 13 Student student = new Student(); 14 // 为学生对象赋值 15 student.setStuName("Wanggc"); 16 student.setStuAge(24); 17 // 建立拷贝目标对象 18 Student destStudent = (Student) copyBean(student); 19 // 输出拷贝结果 20 System.out.println(destStudent.getStuName() + ":" 21 + destStudent.getStuAge()); 22 } 23 24 /** 25 * 拷贝学生对象信息。 26 * 27 * @param from 28 * 拷贝源对象 29 * @param dest 30 * 拷贝目标对象 31 * @throws Exception 32 * 例外 33 */ 34 private static Object copyBean(Object from) throws Exception { 35 // 取得拷贝源对象的Class对象 36 Class<?> fromClass = from.getClass(); 37 // 取得拷贝源对象的属性列表 38 Field[] fromFields = fromClass.getDeclaredFields(); 39 // 取得拷贝目标对象的Class对象 40 Object ints = fromClass.newInstance(); 41 for (Field fromField : fromFields) { 42 // 设置属性的可访问性 43 fromField.setAccessible(true); 44 // 将拷贝源对象的属性的值赋给拷贝目标对象相应的属性 45 fromField.set(ints, fromField.get(from)); 46 } 47 48 return ints; 49 } 50 } 51 52 /** 53 * 学生类。 54 */ 55 class Student { 56 /** 姓名 */ 57 private String stuName; 58 /** 年龄 */ 59 private int stuAge; 60 61 /** 62 * 获取学生姓名。 63 * 64 * @return 学生姓名 65 */ 66 public String getStuName() { 67 return stuName; 68 } 69 70 /** 71 * 设置学生姓名 72 * 73 * @param stuName 74 * 学生姓名 75 */ 76 public void setStuName(String stuName) { 77 this.stuName = stuName; 78 } 79 80 /** 81 * 获取学生年龄 82 * 83 * @return 学生年龄 84 */ 85 public int getStuAge() { 86 return stuAge; 87 } 88 89 /** 90 * 设置学生年龄 91 * 92 * @param stuAge 93 * 学生年龄 94 */ 95 public void setStuAge(int stuAge) { 96 this.stuAge = stuAge; 97 } 98 }
此例和上例运行的结果是相同的。但是copyBean方法返回的对象不再是外面传入的,而是由方法内部产生的,如第40行代码所示。注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:fromClass.getDeclaredConstructor(int.class,String.class).newInstance(24,"wanggc");
至此,Java反射机制的常用机能(运行时调用对象的方法、类属性的使用、创类类的对象)已经介绍完了。
补充:在获得类的方法、属性、构造函数时,会有getXXX和getgetDeclaredXXX两种对应的方法。之间的区别在于前者返回的是访问权限为public的方法和属性,包括父类中的;但后者返回的是所有访问权限的方法和属性,不包括父类的。