反射概念
反射(Reflection)在我们日常开发中无时无刻,被大量运用在框架代码和工具代码中,反射可以通俗点讲就是一个类的自我剖析,通过反射可以获取到这个类所有信息,包括:属性,属性值,方法,构造器等等。
反射机制:
- Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
- Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
反射的应用场景:反射是框架设计的灵魂,像 Spring/Spring Boot、Mybatis 等等框架中都大量使用了反射机制。
反射的优缺点:
- 优点:可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
- 缺点:让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。
反射常用方法
反射机制相关类
| 类 | 含义 |
|---|---|
| java.lang.Class | 代表整个字节码。代表一个类型,代表整个类。 |
| java.lang.reflect.Method | 代表字节码中的方法字节码。代表类中的方法。 |
| java.lang.reflect.Constructor | 代表字节码中的构造方法字节码。代表类中的构造方法。 |
| java.lang.reflect.Field | 代表字节码中的属性字节码。代表类中的成员变量。 |
获取Class对象
1. 知道具体类的情况下可以使用xx.class:
Class class1 = TargetObject.class;
2. 通过 Class.forName()传入类的全路径获取:
Class class2 = Class.forName("com.qing.TargetObject");
3. 通过对象实例xx.getClass()获取:
TargetObject targetObject = new TargetObject();
Class class3 = targetObject.getClass();
获取类的构造方法
| 方法名 | 描述 |
|---|---|
| getConstructors() | 获取类的全部构造器(只能获取public修饰的) |
| getDeclaredConstructors() | 获取类的全部构造器(无视修饰,存在就可获取) |
| getConstructor(Class...<?> parameterTypes) | 获取类的某个构造器(只能获取public修饰的) |
| getDeclaredConstructor(Class...<?> parameterTypes) | 获取类的某个构造器(无视修饰,存在就可获取) |
获取类的成员变量
| 方法名 | 描述 |
|---|---|
| getFields(); | 获取类的全部成员变量(只能获取public修饰的) |
| getDeclaredFields(); | 获取类的全部成员变量(无视修饰,存在就可获取) |
| getField(String name); | 获取类的某个成员变量(只能获取public修饰的) |
| getDeclaredField(String name); | 获取类的某个成员变量(无视修饰,存在就可获取) |
获取类的成员方法
| 方法名 | 描述 |
|---|---|
| getMethods(); | 获取类的全部成员方法(只能获取public修饰的) |
| getDeclaredMethods(); | 获取类的全部成员方法(无视修饰,存在就可获取) |
| getMethod(String name, Class...<?> parameterTypes) | 获取类的某个成员方法(只能获取public修饰的) |
| getDeclaredMethod(String name, Class...<?> parameterTypes) | 获取类的某个成员方法(无视修饰,存在就可获取) |
访问私有变量或方法
私有变量或私有方法对象的 setAccessible(true) 方法设置为true,代表压制Java访问检查,否则会出非法访问异常
field.setAccessible(true);
method.setAccessible(true);
反射相关基本操作实例
- 创建一个我们要使用反射操作的类
TargetObject
package com.qing;
public class TargetObject {
private String value;
public TargetObject() {
value = "Qing";
}
public void publicMethod(String s) {
System.out.println("I love " + s);
}
private void privateMethod() {
System.out.println("value is " + value);
}
}
- 使用反射操作这个类的方法以及参数
package com.qing;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
Class<?> targetClass = Class.forName("com.qing.TargetObject");
TargetObject targetObject = (TargetObject) targetClass.newInstance();
/**
* 获取 TargetObject 类中定义的所有方法
*/
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
/**
* 获取指定方法并调用
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",
String.class);
publicMethod.invoke(targetObject, "Qing");
/**
* 获取指定参数并对参数进行修改
*/
Field field = targetClass.getDeclaredField("value");
//为了对类中的参数进行修改我们取消安全检查
field.setAccessible(true);
field.set(targetObject, "Qing");
/**
* 调用 private 方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用private方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}
}
输出内容:
publicMethod
privateMethod
I love Qing
value is Qing