在计算机学科中,反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。通过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送类的成员变量
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());
}
}
运行结果