反射 reflect
字数: 0 字 时长: 0 分钟
Java 反射机制是 Java 的核心特性之一,它提供了一种在运行时动态获取和操作类信息的能力,实现灵活的动态编程。
Class 类
Class
对象是反射的基石:JVM 在类加载阶段(Loading
)会通过类加载器(ClassLoader
)生成一个 java.lang.Class
对象,这个对象记录了类的元数据信息(成员变量、方法、构造方法等)。
- 针对已编写好的
.java
源文件进行编译 (使用javac.exe
),生成对应的.class
文件。 - 接着,我们使用
java.exe
命令对指定的.class
文件进行解释运行。 - 这个解释运行的过程中,我们需要将
.class
字节码文件加载(使用类的加载器)到内存中(Java 8 之前在方法区/Java 8 之后在元空间)。 - 加载到内存中的
.class
文件对应的结构就是Class
的一个实例。比如:加载到内存中的Person
类或String
类,都作为Class
的一个一个的实例。
提示
运行时类在内存中会缓存起来,在整个执行期间,只会加载一次 (更准确的说,同一个类只能被同一个类加载器加载一次)
核心实现流程 (以调用方法为例)
- 获取
Class
对象
Class
对象存储了类元信息,是反射的入口
获取
Class
对象的几种方式
//1、调用运行时类的静态属性
Class clazz1 = Person.class; //运行时类
//2、调用运行时类的对象的 getClass()
Person person = new Person();
Class clazz2 = person.getClass();
//3、Class.forName("全类名")
Class clazz3 = Class.forName("com.ttdxg.reflect.Person");
//4、类的加载器
Class<?> clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.ttdxg.reflect.Person");
- 获取
Method
对象
java.lang.reflect
包提供了操作 Class
对象内元数据的核心反射类:Field
(字段) 、Method
(方法)、Constructor
(构造方法)、Modifier
(修饰符,如private
)等
注意
这些 Field
、Method
、Constructor
对象本身并不包含具体的对象实例数据或运行时代码的执行能力,他们是元数据的封装器
Method.invoke()
方法调用
Method.invoke()
首先要做安全检查,因此一般使用 setAccessible(true)
来绕过 Java 权限检查
然后 JVM 通过内部机制调用 JNI (Java Native Interface
)本地方法,找到实际方法的入口点,执行目标方法的字节码。
动态代码生成
为了解决每次 invoke
调用大量本地方法导致的性能问题,JVM 在前几次调用本地方法,后续会通过动态字节码生成技术
在内存中动态生成相应的 JAVA 字节码类,其中包含直接将目标方法调用嵌入的高效 invoke
方法
后续对该方法的反射调用会转到这个新生成的 invoke
方法执行,消除了昂贵的本地方法调用开销
但这仍然比直接调用慢,需要权衡反射带来的性能问题
反射 API
创建运行时类的对象
- 要求运行时类必须提供一个空参构造器
- 调用空参构造器的权限必须足够
Class clazz = Person.class;
Person person = class.newInstance();
获取运行时类的完整结构
Class<Person> personClass = Person.class;
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method method : declaredMethods) {
//获取方法声明的注解
Annotation[] annotations = method.getAnnotations();
//权限修饰符
String s = Modifier.toString(method.getModifiers());
//返回值类型
String name = method.getReturnType().getName();
//形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
//异常列表
Class<?>[] exceptionTypes = method.getExceptionTypes();
//获取运行时父类
Class<?> superclass = clazz.getSuperclass();
//获取带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
//获取运行时类实现的接口
Class<?>[] interfaces = clazz.getInterfaces();
//获取运行时类所在的包
Package aPackage = clazz.getPackage();
}
获取运行时类的父类的泛型 (难点)
//获取带泛型的父类 (Type 是一个接口, Class 实现了此接口)
Type superclass = clazz.getGenericSuperclass();
//带泛型的父类可以强转位 ParameterizedType
ParameterizedType parameterizedType = (ParameterizedType) superclass;
//获取泛型参数
Type[] arguments = parameterizedType.getActualTypeArguments();
//获取泛型参数名称
String name = ((Class) arguments[0]).getName();
调用指定的属性
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
//获取运行时类指定名的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(person,"Tom");
//如果是静态变量,直接传 null 即可
name.set(null,"Jerry");
调用指定的方法
Method method = clazz.getDeclaredMethod("annotationMethod",String.class);
method.setAccessible(true);
method.invoke(person,"Tom");
调用指定的构造器
Class<Person> clazz = Person.class;
//1、获取指定参数的构造器
Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);
//2、确保权限足够
declaredConstructor.setAccessible(true);
//构造器创建实例
Person person = declaredConstructor.newInstance("Tom", 12);
获取指定的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String columnName();
String columnType();
}
@Data
@Table(value = "person")
public class Person {
@Column(columnName = "name", columnType = "varchar(10)")
private String name;
@Column(columnName = "age", columnType = "int")
private int age = 1;
}
@Test
public void test1() throws NoSuchFieldException {
Class<Person> clazz = Person.class;
// 获取类上的注解
Table table = clazz.getDeclaredAnnotation(Table.class);
// 获取 name 字段上的注解
Field nameFiled = clazz.getDeclaredField("name");
Column column = nameFiled.getDeclaredAnnotation(Column.class);
}
反射实际应用 -- 动态代理
public class DemoProxy implements InvocationHandler {
//被代理的对象
private Object target;
public Object invoke(Object proxy,Method method,Object[] args) {
return method.invoke(target,args);
}
}