反射
文章目录
- 反射
- 前言
- 一、反射概念
- 二、反射与Class类
- 获取Class对象方式
- 三、反射访问构造方法
- 1、获取包名
- 2、获取类名
- 3、获取父类名称
- 4、获取接口
- 5、获取类中构造方法
- 1)获取所有的构造方法
- 2)获取public修饰的构造方法
- 3)获取private修饰的构造方法
- 4)获取无参构造方法
- 5)获取有参构造方法
- 四、反射访问成员变量
- 1、访问所有的成员变量
- 2、访问特定的成员变量
- 3、访问公共的成员变量
- 4、访问私有的成员变量
- 五、反射访问成员方法
- 1、获取类中所有的成员方法
- 2、类中所有public修饰的方法
- 3、获取并调用无参方法
- 4、获取并调用有参方法
- 5、获取并调用私有方法
前言
在计算机学科中,反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。通过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属性
/*
例如现在有一个Student类,类中有属性、构造方法、普通方法等等
现在把Student类中的内容存储到Class类对象中,既然是对象就可以通过方法获取里面的内容
*/
/*
public final Class<?> getClass()返回此 Object 的运行时类
*/
/*
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象
*/
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对象方式
public class GetClassDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
/*
Person继承Object类
public final Class<?> getClass()返回此 Object 的运行时类
获取Class对象的方式一:使用Object类中的getClass()方法
*/
Person person1 = new Person();
Class personClass1 = person1.getClass();
System.out.println("personClass1 : " + personClass1);
/*
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象
获取Class对象的方式二:使用Class类中的forName(String className)方法
注意:className必须是完整路径名
*/
Class personClass2 = Class.forName("javaapidemo0219.entity.Person");
System.out.println("personClass2 : " + personClass2);
/*
获取Class对象的方式三:使用Person类中默认存在的class属性(静态属性)
*/
Class personClass3 = Person.class;
System.out.println("personClass3 : " + personClass3);
/*
输出结果
personClass1 : class javaapidemo0219.entity.Person
personClass2 : class javaapidemo0219.entity.Person
personClass3 : class javaapidemo0219.entity.Person
*/
// 判断获取的Class对象是否是同一个对象
System.out.println(personClass1 == personClass2); // true
System.out.println(personClass2 == personClass3); // true
/*
说明:上面3种方式做的是同一件事,将Person类中的内容(属性、构造方法、成员方法)存储在Class类对象(就是一个容器)中
然后就可以通过Class对象调用相关方法对Person类中的内容进行操作
*/
}
}
三、反射访问构造方法
// 首先通过Class类中forName()方法将Person类装载到Class类对象中
Class personClass = Class.forName("javaapidemo0219.entity.Person");
1、获取包名
首先调用Class类中getPackage()方法,返回值为Package类
然后调用Package类中getName()方法,返回值为String
// 获取Person类所在的包的名称
// public Package getPackage()获取此类的包
Package personClassPackage = personClass.getPackage();
// String getName()返回此包的名称
String personClassPackageName = personClassPackage.getName();
System.out.println("包名 : " + personClassPackageName); // javaapidemo0219.entity
2、获取类名
直接调用Class类中getName()方法,返回值为String
// 获取加载的类的名称
// String getName() 以String的形式返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
String className = personClass.getName();
System.out.println("类名 : " + className); // javaapidemo0219.entity.Person
3、获取父类名称
首先调用Class类中getSuperclass()方法,返回值为Class类
接着调用Class类中getName()方法,返回值为String
// 获取加载的类的父类
/*
public Class<? super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
*/
Class personClassSuperclass = personClass.getSuperclass();
// 获取父类名
String personClassSuperclassName = personClassSuperclass.getName();
System.out.println("父类名称 :" + personClassSuperclassName); // java.lang.Object
4、获取接口
首先调用Class类中getInterfaces()方法,返回值是一个数组
(数组中接口对象顺序与此对象所表示的类的声明的 implements 子句中接口名顺序一致)
然后遍历数组使用getName()方法
// 获取加载的类实现的接口
// Class<?>[] getInterfaces()确定此对象所表示的类或接口实现的接口
Class[] personClassInterfaces = personClass.getInterfaces();
// 遍历数组,获取所有实现的接口的名称
for (Class clazz : personClassInterfaces) {
System.out.println("实现的接口名称 :" + clazz.getName());
// 实现的接口名称 :java.io.Serializable
}
案例代码
public class ClassDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
// 首先通过Class类中forName()方法将Person类装载到Class类对象中
Class personClass = Class.forName("javaapidemo0219.entity.Person");
// 获取Person类所在的包的名称
// public Package getPackage()获取此类的包
Package personClassPackage = personClass.getPackage();
// String getName()返回此包的名称
String personClassPackageName = personClassPackage.getName();
System.out.println("包名 : " + personClassPackageName); // 包名 : javaapidemo0219.entity
// 获取加载的类的名称
// String getName() 以String的形式返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
String className = personClass.getName();
System.out.println("类名 : " + className); // 类名 : javaapidemo0219.entity.Person
// 获取加载的类的父类
// public Class<? super T> getSuperclass()返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
Class personClassSuperclass = personClass.getSuperclass();
// 获取父类名称
String personClassSuperclassName = personClassSuperclass.getName();
System.out.println("父类名称 :" + personClassSuperclassName); // 父类名称 :java.lang.Object
// 获取加载的类实现的接口
// Class<?>[] getInterfaces()确定此对象所表示的类或接口实现的接口
Class[] personClassInterfaces = personClass.getInterfaces();
// 遍历数组,获取所有实现的接口的名称
for (Class clazz : personClassInterfaces) {
System.out.println("实现的接口名称 :" + clazz.getName()); // 实现的接口名称 :java.io.Serializable
}
}
}
/*
包名 : javaapidemo0219.entity
类名 : javaapidemo0219.entity.Person
父类名称 :java.lang.Object
实现的接口名称 :java.io.Serializable
*/
5、获取类中构造方法
1)获取所有的构造方法
首先调用Class类中getDeclaredConstructors()方法,返回值为Constructor 对象数组
然后遍历数组,数组中的元素为Constructor类对象,调用方法获取构造方法修饰符、构造方法名、构造方法形参
// 使用Object类中的getClass()方法获取Class对象,将Person类加载到Class对象中
Person person = new Person();
Class personClass = person.getClass();
// 获取所有构造方法,包括public、private修饰的构造方法
/*
public Constructor<?>[] getDeclaredConstructors()throws SecurityException
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法
它们是公共、保护、默认(包)访问和私有构造方法
*/
Constructor[] constructors = personClass.getDeclaredConstructors();
// 遍历数组,输出数组中每一个构造方法的名称、访问修饰符、形参名称
for (Constructor constructor : constructors) {
// 获取构造方法名
System.out.println("构造方法名:" + constructor.getName());
// 获取构造方法的修饰符
int num = constructor.getModifiers();
System.out.println("\t构造方法修饰符:" + (num == 1 ? "public" : "private"));
// public = 1 private = 2
// 获取构造方法的形参名称
/*
public Class<?>[] getParameterTypes()
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型
*/
Class[] types = constructor.getParameterTypes();
for (Class type : types) {
// 获取各个参数的名称
System.out.println("\t构造方法形参名称:" + type.getName());
}
}
/*
构造方法名:javaapidemo0219.entity.Person
构造方法修饰符:private
构造方法形参名称:java.lang.String
构造方法名:javaapidemo0219.entity.Person
构造方法修饰符:public
构造方法形参名称:java.lang.String
构造方法形参名称:int
构造方法名:javaapidemo0219.entity.Person
构造方法修饰符:public
*/
2)获取public修饰的构造方法
首先调用Class类中getConstructors()方法,返回值为Constructor 对象数组
// 获取Person类中所有的public修饰的构造方法
/*
Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法
*/
Constructor[] personClassConstructors = personClass.getConstructors();
// 遍历数组,输出所有构造方法的名称、访问修饰符
for (Constructor constructor : personClassConstructors) {
// 获取构造方法名
String constructorName = constructor.getName();
System.out.println("构造方法名 : " + constructorName);
// 获取构造方法的修饰符
int num = constructor.getModifiers();
System.out.println("\t构造方法修饰符 : " + (num == 1 ? "public" : "private"));
// 获取每个构造方法的形参列表
/*
public Class<?>[] getParameterTypes()
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型
*/
Class[] parameterTypes = constructor.getParameterTypes();
// 遍历数组,获取每个构造方法的形参名称
for (Class type : parameterTypes) {
System.out.println("\t形参名称 : " + type.getName());
}
}
/*
构造方法名 : javaapidemo0219.entity.Person
构造方法修饰符 : public
形参名称 : java.lang.String
形参名称 : int
构造方法名 : javaapidemo0219.entity.Person
构造方法修饰符 : public
*/
3)获取private修饰的构造方法
Class类中没有专门的方法返回私有的构造方法
可以获取所有的构造方法(getDeclaredConstructors()方法)进行判断
Person person = new Person();
Class personClass = person.getClass();
// Class类中方法
Constructor[] constructors = personClass.getDeclaredConstructors();
for (Constructor constructor : constructors) {
// 获取构造方法的修饰符
int num = constructor.getModifiers();
if (num == 2) {
// 获取构造方法名
System.out.println("构造方法名:" + constructor.getName());
//
System.out.println("\t构造方法修饰符 : " + (num == 1 ? "public" : "private"));
Class[] parameterTypes = constructor.getParameterTypes();
// 遍历数组,获取每个构造方法的形参名称
for (Class type : parameterTypes) {
System.out.println("\t形参名称 : " + type.getName());
}
}
}
/*
构造方法名:javaapidemo0219.entity.Person
构造方法修饰符 : private
形参名称 : java.lang.String
*/
4)获取无参构造方法
Class类中的getConstructor()方法
然后使用newInstance()方法,Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例
// 使用Object类中的getClass()方法获取Class对象,将Person类加载到Class对象中
Person person = new Person();
Class personClass = person.getClass();
// 获取Person类中的public修饰的无参构造方法
/*
public Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法
*/
Constructor personClassConstructor1 = personClass.getConstructor();
// 通过personClassConstructor1对象调用方法,可以获取Person类中构造方法的相关信息
// 获取构造方法的名称
// String gertName()以字符串形式返回此构造方法的名称
String personClassConstructor1Name = personClassConstructor1.getName();
System.out.println("构造方法名 : " + personClassConstructor1Name);
// 构造方法名 : javaapidemo0219.entity.Person
// 获取构造方法的访问修饰符
// public int getModifiers()返回此类或接口以整数编码的 Java 语言修饰符
int num = personClassConstructor1.getModifiers();
System.out.println(num == 1 ? "public" : "private"); // public
// java.lang.reflect.Modifier public = 1 private = 2
// 通过获取的无参构造方法创建对象
/*
public T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
*/
Person person1 = (Person)personClassConstructor1.newInstance();
System.out.println("person1 : " + person1); // person1 : Person{name='张三', age=18}
5)获取有参构造方法
// 获取Person类中带有两个参数的public修饰的有参构造方法
Constructor constructor = personClass.getConstructor(String.class,int.class);
System.out.println(constructor);
// public javaapidemo0219.entity.Person(java.lang.String,int)
// 通过该构造方法创建对象
Person person2 = (Person)constructor.newInstance("李二狗",21);
System.out.println(person2); // Person{name='李二狗', age=21}
四、反射访问成员变量
1、访问所有的成员变量
包括public、private修饰的成员变量
调用Class类中的getDeclaredFields()方法,返回值为Field对象数组
import javaapidemo0219.entity.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
public class ClassDemo02 {
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class personClass = Class.forName("javaapidemo0219.entity.Person");
//
Constructor personClassConstructor = personClass.getConstructor(String.class, int.class);
Person person = (Person) personClassConstructor.newInstance("牛", 24);
// Field[] getDeclaredFields()返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
Field[] personClassFields = personClass.getDeclaredFields();
for (Field field : personClassFields) {
// 输出属性名
// String getName()返回此 Field 对象表示的字段的名称
System.out.println("属性名 :" + field.getName());
// 输出属性的数据类型
// Class<?> getType()返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型
System.out.println("属性数据类型 :" + field.getType().getName());
// 输出访问修饰符
System.out.println("属性访问修饰符 :" + ((field.getModifiers() == 1) ? "public" : "private"));
/*
属性名 :name
属性数据类型 :java.lang.String
属性访问修饰符 :public
属性名 :age
属性数据类型 :int
属性访问修饰符 :private
*/
// 获取属性的值
/*if (field.getType().equals(String.class)) {
System.out.println(field.get(person));
} else if (field.getType().equals(int.class)) {
System.out.println(field.get(person));
}*/ // 可以输出name属性值,无法获取private修饰的age属性值
// 但是在反射中可以暴力破解
if (field.getType().equals(String.class)) {
System.out.println(field.get(person));
} else if (field.getType().equals(int.class)) {
// Person类中age属性被private修饰了,不能直接访问,需要进行暴力破解
if (field.getModifiers() == Modifier.PRIVATE) {
/*
public void setAccessible(boolean flag)
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
*/
field.setAccessible(true);
System.out.println(field.get(person));
}
}
/*
属性名 :name
属性数据类型 :java.lang.String
属性访问修饰符 :public
牛
属性名 :age
属性数据类型 :int
属性访问修饰符 :private
24
*/
}
}
}
2、访问特定的成员变量
调用Class类中getField()方法,返回Field对象
import javaapidemo0219.entity.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ClassDemo03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class personClass = Class.forName("javaapidemo0219.entity.Person");
// 获取特定的属性值并修改
// 创建一个Person类对象,需要通过构造方法
Constructor personClassConstructor =personClass.getConstructor(String.class,int.class);
Person person =(Person)personClassConstructor.newInstance("王五",22);
// 获取Person类中指定的属性
/*
public Field getField(String name)throws NoSuchFieldException,SecurityException
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段
*/
Field nameField = personClass.getField("name");
System.out.println(nameField.getName()+"-"+nameField.getType().getName()+"-"+nameField.getModifiers());
// public Object get(Object obj)返回指定对象上此 Field 表示的字段的值
System.out.println(nameField.get(person));
//
nameField.set(person,"李二狗");
Field ageField =personClass.getDeclaredField("age");
//
ageField.setAccessible(true);
System.out.println(ageField.get(person));
ageField.set(person,33);
System.out.println(person);
}
}
/*
name-java.lang.String-1
王五
22
Person{name='李二狗', age=33}
*/
3、访问公共的成员变量
调用Class类中getFields()方法获取Class 对象所表示的类或接口的所有可访问公共字段
import java.lang.reflect.Field;
public class ClassDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
Class personClass = Class.forName("javaapidemo0219.entity.Person");
// 获取Person类中的成员变量(public修饰的成员变量)
/*
public Field[] getFields()throws SecurityException
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段
*/
Field[] personClassFields = personClass.getFields();
// 遍历数组,获取数组中每一个字段的访问修饰符、数据类型、属性名
for (Field field : personClassFields) {
// 输出属性名
// String getName()返回此 Field 对象表示的字段的名称
System.out.println("属性名 :" + field.getName());
// 输出属性的数据类型
// Class<?> getType()返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型
System.out.println("属性数据类型 :" + field.getType().getName());
// 输出访问修饰符
System.out.println("属性访问修饰符 :" + ((field.getModifiers() == 1) ? "public" : "private"));
}
}
}
4、访问私有的成员变量
getDeclaredField(String name)方法
// 访问私有成员变量的属性值和修改属性值需要暴力破解
五、反射访问成员方法
1、获取类中所有的成员方法
包括public、private修饰的方法,但不包括父类的方法
调用Class类中getDeclaredMethods()方法
// 通过反射获取Person类中所有public修饰的方法(获得所有方法,包括private修饰的方法,但不包括父类的方法)
Method[] personClassMethods = personClass.getDeclaredMethods();
2、类中所有public修饰的方法
类中所有public修饰的方法,包括父类中public修饰的方法
调用Class类中getMethods()方法
// 通过反射获取Person类中所有public修饰的方法(包括父类Object类中public修饰的方法)
Method[] personClassMethods = personClass.getMethods();
3、获取并调用无参方法
调用Class类中getMethod(String name)方法
// 通过反射获取Person类中的无参eat()方法
Method eatMethod1 = personClass.getMethod("eat");
// 调用获取的这个eat方法
/*
public Object invoke(Object obj,Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
*/
eatMethod1.invoke(person);//等价与person.eat();
4、获取并调用有参方法
调用Class类中getMethod(String name, Class)方法
// 通过反射获取Person了中的有参eat()方法
Method eatMethod2 = personClass.getMethod("eat", int.class);
// 调用获取的这个eat方法
eatMethod2.invoke(person, 3); // 等价与person.eat(3); 参数
5、获取并调用私有方法
调用Class类中getDeclaredMethod(String name)方法
// 通过反射获取Person类中的sleep()方法
Method sleepMethod1 = personClass.getDeclaredMethod("sleep");
// sleepMethod1.invoke(person);//IllegalAccessException
// 暴力破解私有方法(跳过访问修饰符的检查)
sleepMethod1.setAccessible(true);
sleepMethod1.invoke(person);
import javaapidemo0219.entity.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ClassDemo04 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 使用Person类中默认的class属性获取Class对象
Class personClass = Person.class;
// 使用无参构造创建一个Person类对象
Person person = new Person();
System.out.println(person);
System.out.println ("------ ------ ------");
// 通过反射获取Person类中所有public修饰的方法(包括父类Object类中public修饰的方法)
// Method[] personClassMethods = personClass.getMethods();
// 通过反射获取Person类中所有public修饰的方法(获得所有方法,包括private修饰的方法,但不包括父类的方法)
Method[] personClassMethods = personClass.getDeclaredMethods();
for (Method method : personClassMethods) {
// 获取方法名称
System.out.println("方法名:" + method.getName());
// 获取方法返回值
System.out.println("\t方法返回值类型:" + method.getReturnType().getName());
// 获取方法的参数列表
Class[] methodParameterTypes = method.getParameterTypes();
// 遍历数组,获取每个方法的形参列表名称
for (Class type : methodParameterTypes) {
System.out.println("\t参数名:" + type.getName());
}
// 获取方法的访问修饰符
System.out.println("\t方法修饰符:" + method.getModifiers());
}
System.out.println("------------------------------------------------");
// 通过反射获取Person类中的无参eat()方法
Method eatMethod1 = personClass.getMethod("eat");
// 调用获取的这个eat方法
eatMethod1.invoke(person);//等价与person.eat();
// 通过反射获取Person了中的有参eat()方法
Method eatMethod2 = personClass.getMethod("eat", int.class);
// 调用获取的这个eat方法
eatMethod2.invoke(person, 3); // 等价与person.eat(3);
// 通过反射获取Person类中的sleep()方法
Method sleepMethod1 = personClass.getDeclaredMethod("sleep");
// sleepMethod1.invoke(person); // IllegalAccessException
// 暴力破解私有方法(跳过访问修饰符的检查)
sleepMethod1.setAccessible(true);
sleepMethod1.invoke(person);
}
}
/*
Person{name='张三', age=18}
------ ------ ------
方法名:toString
方法返回值类型:java.lang.String
方法修饰符:1
方法名:getName
方法返回值类型:java.lang.String
方法修饰符:1
方法名:setName
方法返回值类型:void
参数名:java.lang.String
方法修饰符:1
方法名:sleep
方法返回值类型:void
方法修饰符:2
方法名:getAge
方法返回值类型:int
方法修饰符:1
方法名:setAge
方法返回值类型:void
参数名:int
方法修饰符:1
方法名:eat
方法返回值类型:void
参数名:int
方法修饰符:1
方法名:eat
方法返回值类型:void
方法修饰符:1
------ ------ ------
今天吃了1顿大餐
今天吃了3顿大餐
非常舒服的睡了一觉
*/