在计算机学科中,反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。通过Java的反射机制,程序员可以更深入地控制程序的运行过程,如在程序运行时对用户输入的信息进行验证,还可以逆向控制程序的执行过程。理解反射机制是学习Spring框架的基础。
一、反射简介
(1)Java的反射机制是一种动态获取信息以及动态调用对象的方法的功能。
(2)具体来说,在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
(3)通过Java反射机制,就可以在程序中访问已经装载到JVM中的Java对象的各种描述,能够访问、检测和修改描述Java对象本身信息。
(4)Java在java.lang.reflect包中提供了对该功能的支持。
二、反射与Class类
(1)反射与Class类息息相关。Class对象封装了它所代表的类的描述信息,通过调用Class对象的各种方法,就可以获取到Class对象所代表的类的主要描述信息。
(2)获取目标类的Class对象,有以下3种方法可以实现:
-
使用Object类的getClass()方法
-
使用Class类的forName(String url)方法,用目标类的完整路径作为参数
-
使用目标类的class属性
在com.cxyzxc.reflect包中定义Person类
package com.cxyzxc.www.entity; public class Person { //公共属性 public String name; //私有属性 private int age; //无参构造 public Person() { this.name = "张三"; this.age = 18; } //有参构造 public Person(String name, int age) { this.name = name; this.age = age; } //私有有参构造 private Person(String name) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } //无参方法 public void eat(){ System.out.println("今天吃了1顿大餐"); } //带参方法 public void eat(int num){ System.out.println("今天吃了"+num+"顿大餐"); } //私有方法 private void sleep(){ System.out.println("非常舒服的睡了一觉"); } }
获取Class类对象
package com.cxyzxc.www.reflect; import com.cxyzxc.www.entity.Person; public class Demo01GetClass { public static void main(String[] args) throws ClassNotFoundException { //获取Class对象 //方法一:使用Class.forName()方法 Class personClass1 =Class.forName("com.cxyzxc.www.entity.Person"); //方法二:使用类的class属性 Class personClass2 = Person.class; //方法三:使用Object类中的getClass()方法 Person person = new Person(); Class personClass3 = person.getClass(); //使用Class.forName()方法和使用类的class属性获取的Class对象是同一个对象 System.out.println("personClass1和personClass2是同一个对象:"+(personClass1==personClass2));//true //使用类的class属性和使用Object类中的getClass()方法获取的Class对象是同一个对象 System.out.println("personClass1和personClass2是同一个对象:"+(personClass2==personClass3));//true } }
(3)Class对象常用方法如下表所示:
方法 | 功能描述 |
---|---|
Package getPackage() | 获得类所在的包路径 |
String getName() | 获得类(或接口、数组等)的名称 |
Class getSuperclass() | 获得该类的父类的Class对象 |
Class getInterfaces() | 获得该类实现的所有接口 |
Constructor[] getConstructors() | 获得该类的public修饰的构造方法,按声明顺序返回 |
Constructor getConstructor(Class<?>...ParameterType) | 获得该类的public修饰的特定参数列表的构造方法 |
Constructor[] getDeclaredConstructors() | 获得该类的所有构造方法 |
Constructor getDeclaredConstructors(Class<?>...ParameterType) | 获得该类的特定参数列表的构造方法 |
Method[] getMethods() | 获得该类的所有public修饰的方法 |
Method getMethod(String name,Class<?>...ParameterType) | 获得该类的public修饰的特定参数列表的方法 |
Method[] getDeclaredMethods() | 获得该类的所有方法,包含private修饰的方法,按声明顺序返回 |
Method[] getDeclaredMethods(String name,Class<?>...ParameterType) | 获得该类的特定参数列表的方法,包括private修饰的方法 |
Field[] getFields() | 获得该类的所有public修饰的成员变量 |
Field getField(String name) | 获得该类的public修饰的特定名称的成员变量 |
Field[] getDeclaredFields() | 获得该类的所有成员变量,包括private修饰的成员变量,按声明顺序返回 |
Field getDeclaredField(String name) | 获得特定名称的成员变量 |
三、反射访问构造方法
使用Class对象的如下方法,将返回Constructor类型对象或数组,每个Constructor代表一个构造方法。
方法 | 功能描述 |
---|---|
Constructor getConstructor() | 返回Constructor,匹配public修饰的不带参的构造方法 |
Constructor[] getConstructors() | 返回Constructor数组,匹配public修饰的所有构造方法 |
Constructor getConstructor(Class<?>...ParameterType) | 返回Constructor,匹配和参数列表相同的构造方法 |
Constructor[] getConstructors(Class<?>...ParameterType) | 返回Constructor数组,匹配和参数列表相同的构造方法 |
Constructor getDeclaredConstructor() | 返回Constructor,匹配包括private修饰的不带参的构造方法 |
Constructor[] getDeclaredConstructors() | 返回Constructor数组,匹配包括private修饰的构造方法 |
Constructor getDeclaredConstructor(Class<?>...parameterTypes) | 返回Constructor,匹配包括private修饰的和参数列表相同的构造方法 |
Constructor[] getDeclaredConstructors(Class<?>...parameterTypes) | 返回Constructor数组,匹配包括private修饰的和与参数列表想用的构造方法 |
其中,带有Class<?>...parameterType字样的方法代表构造方法的参数列表,实际运用时需要指定具体的类型。
Constructor类常用方法如下所示:
方法 | 功能描述 |
---|---|
boolean isVarargs() | 查看该构造方法是否带有可变数量的参数 |
Class[] getParameterTypes() | 获得该构造方法的各个参数的类型 |
Class[] getExceptionTypes() | 获得该构造方法可能抛出的异常类型 |
Object newInstance(Object...initargs) | 通过该构造方法利用指定参数创建一个该类的对象,如果未设置参数,则表示采用默认无参数的构造方法 |
void setAccessible(boolean flag) | 如果该构造方法的权限为private,默认不允许通过反射利用newInstance(Object...iniargs)方法创建对象。如果先执行该方法,并将入口参数设为true,则允许创建 |
int getModifiers() | 返回构造方法所用修饰符的整数 |
使用反射访问Person类的构造方法
package com.cxyzxc.www.reflect; import com.cxyzxc.www.entity.Person; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Demo02GetConstructors { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //获取Class对象 Class<Person> personClass1 = Person.class; System.out.println("--------------输出所有public修饰的构造方法--------------"); //获得所有public修饰的构造方法 Constructor[] constructors1 = personClass1.getConstructors(); //遍历所有的构造方法 for(Constructor constructor:constructors1){ //输出构造方法对象 System.out.println("构造方法:"+constructor); //获取构造方法的参数列表 Class[] types = constructor.getParameterTypes(); //遍历当前构造方法的参数列表 for(int i = 0 ;i<types.length;i++){ System.out.println("\t参数"+(i+1)+"类型:"+types[i]); } } System.out.println("--------------输出所有构造方法--------------"); //获取所有构造方法 Constructor[] constructors2 =personClass1.getDeclaredConstructors(); //遍历输出所有Construct对象 for (Constructor constructor:constructors2){ //输出构造方法对象 System.out.println("构造方法:"+constructor); //输出构造方法修饰符 System.out.println("\t构造方法修饰符:"+(constructor.getModifiers()==1?"public":"private")); //获取构造方法的参数列表 Class[] types = constructor.getParameterTypes(); //遍历当前构造方法的参数列表 for(int i = 0 ;i<types.length;i++){ System.out.println("\t参数"+(i+1)+"类型:"+types[i]); } } System.out.println("--------------访问特定的构造方法--------------"); System.out.println("-------无参构造方法-------"); //获取无参构造方法 Constructor constructor1 =personClass1.getConstructor(); //通过无参构造方法创建Person对象 Person person1=(Person) constructor1.newInstance(); System.out.println(person1); System.out.println("-------有参构造方法-------"); //获取有参构造方法 Constructor constructor2 =personClass1.getConstructor(String.class,int.class); //通过有参构造方法创建对象 Person person2=(Person)constructor2.newInstance("王五",25); System.out.println(person2); } }
四、反射访问成员变量
使用Class对象的如下方法,将返回Field型对象或数组。Field对象代表一个成员变量,利用Field对象可以访问和操纵相应的成员变量。
方法 | 功能描述 |
---|---|
getFields() | 获得所有的public修饰的成员变量 |
getFields(String name) | 获得指定名称的public修饰的成员变量 |
getDeclareFields() | 获得所有的成员变量,包括private修饰的成员变量 |
getDeclaredField(String name) | 获得指定名称的成员变量,包括private修饰的成员变量 |
Field类的常用方法
方法 | 功能描述 |
---|---|
String getName() | 返回该成员变量的名称 |
Class getType() | 返回表示该成员变量的类型的Class对象 |
Object get(Object obj) | 返回指定对象中成员变量的值 |
void set(Object obj,Object value) | 设置对象obj中成员变量的值为value |
int getInt(Object obj) | 返回指定对象obj中类型为int的成员变量的值 |
void setInt(Object obj,int value) | 设置对象obj中类型为int的成员变量的值为value |
float getFloat(Object obj) | 返回指定对象obj中类型为float的成员变量的值 |
setFloat(Object obj,float value) | 设置对象obj中类型为float的成员变量的值为value |
boolean getBoolean(Object obj) | 返回指定对象obj中类型为boolean的成员变量的值 |
setBoolean(Object obj,boolean value) | 设置对象obj中类型为boolean的成员变量的值为value |
getAccessible(boolean flag) | 设置是否允许直接访问private等私有权限的成员变量 |
getModifiers() | 返回成员变量所用修饰符的整数 |
反射访问Person送类的成员变量
package com.cxyzxc.www.reflect; import com.cxyzxc.www.entity.Person; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; public class Demo03GetFields { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { //获取Class对象 Class personClass1 = Class.forName("com.cxyzxc.www.entity.Person"); //获取有参构造方法 Constructor constructor1 =personClass1.getConstructor(String.class,int.class); //通过有参构造方法创建对象 Person person1= (Person) constructor1.newInstance("张三",28); System.out.println("-------------------遍历所有public修饰的属性-------------------"); //获取所有public修饰的成员变量 Field[] publicFields1 =personClass1.getFields(); //遍历所有public修饰的属性 for (Field pubField:publicFields1) { System.out.println("成员变量名称:"+pubField.getName()); System.out.println("成员变量类型:"+pubField.getType()); //获取成员变量的值 if(pubField.getType().equals(String.class)){ System.out.println("成员变量值:"+pubField.get(person1)); }else if(pubField.getType().equals(int.class)){ System.out.println("成员变量值:"+pubField.getInt(person1)); } } System.out.println("-------------------遍历所有属性-------------------"); Field[] fields = personClass1.getDeclaredFields(); for (Field field:fields){ System.out.println("成员变量名称:"+field.getName()); System.out.println("成员变量类型:"+field.getType()); System.out.println("成员变量修饰:"+(field.getModifiers()==1?"public":"private")); //获取成员变量值 if(field.getType().equals(String.class)){ System.out.println("成员变量值:"+field.get(person1)); }else if(field.getType().equals(int.class)){ //如果是私有的成员变量 if(field.getModifiers()== Modifier.PRIVATE){ //设置可以访问。不然会抛出IllegalAccess Exception field.setAccessible(true); } System.out.println("成员变量值:"+field.getInt(person1)); } } System.out.println("-------------------访问特定的成员变量-------------------"); //获取姓名属性 Field field1 = personClass1.getField("name"); //修改姓名属性值 field1.set(person1,"李二狗"); System.out.println(person1); //反射获取私有成员变量,会抛出异常 // Field field2 =personClass1.getFields("age"); // System.out.println(field2); //暴力获取私有字段 Field field2 = personClass1.getDeclaredField("age"); field2.setAccessible(true); field2.set(person1,30); System.out.println(person1); } }
五、反射访问成员方法
使用Class对象的如下方法,将返回Method型对象或数组,Method对象代表一个方法。
方法 | 功能描述 |
---|---|
getMethods() | 获得所有public修饰的方法,包括父类的方法 |
getMethod(String name,Class<?>...parametertTypes) | 获得public修饰的特定名称和参数列表的方法 |
getDeclaredMethods() | 获得所有方法,包括private修饰的方法,但不包括父类的方法 |
getDeclaredMethod(String name,Class<?>..parameterTypes) | 获得特定名称和参数列表的方法,包括private修饰的方法 |
Method类的常用方法
方法 | 功能描述 |
---|---|
String getName() | 返回该方法的名称 |
Class[] getParameterTypes() | 返回该方法的各个参数的类型 |
Class getReturnType() | 返回该方法的返回值的类型 |
Class[] getExceptionTypes() | 返回该方法可能抛出的异常 |
Object invoke(Object obj,Object...args) | 用参数args执行指定对象obj中的该方法 |
boolean isVarArgs() | 查看该成员方法是否带有可变数量的参数 |
int getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
反射访问Person类的成员方法
package com.cxyzxc.www.reflect; import com.cxyzxc.www.entity.Person; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Demo04GetMethods { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { //获取Class对象 Class PersonClass1 = Person.class; Person person1 = new Person(); System.out.println("-----------------------获取所有public修饰的方法(包括Object类中的方法)-----------------------"); Method[] methods1 = PersonClass1.getMethods(); //遍历输出所有的方法 for(Method method : methods1){ //输出方法名称 System.out.println("方法名:"+method.getName()); } System.out.println("-----------------------获取所有方法(不包括Object类中的方法)-----------------------"); Method[] methods2 = PersonClass1.getDeclaredMethods(); //遍历输出所有的方法 for(Method method : methods2){ //输出方法名称 System.out.println("方法名:"+method.getName()); } System.out.println("-----------------------获取指定名称的方法并使用(无参方法)-----------------------"); Method m1 = PersonClass1.getMethod("eat"); //调用方法 m1.invoke(person1);//等价于person.eat() System.out.println("-----------------------获取指定名称的方法并使用(有参方法)-----------------------"); Method m2 = PersonClass1.getMethod("eat",int.class); //调用方法 m2.invoke(person1,5);//等价于person.eat(5) System.out.println("-----------------------获取指定名称的私有方法并使用(私有方法)-----------------------"); Method m3 = PersonClass1.getDeclaredMethod("sleep"); //因为方法是私有方法,要设置访问权限才能访问 m3.setAccessible(true); //调用方法 m3.invoke(person1); System.out.println("-----------------------判断方法的返回值类型-----------------------"); Method m4 = PersonClass1.getMethod("getAge"); System.out.println("方法返回值类型:"+m4.getReturnType()); } }