【Java】反射

反射

什么是反射?

反射是Java提供的一种机制,允许程序在运行时动态地获取类的完整信息,包括:

  • 类的构造方法、成员变量、成员方法
  • 类的修饰符、父类、实现的接口
  • 类上的注解信息

反射相关的类⭐

反射的主要功能集中在java.lang.reflect包中,包括ConstructorMethodField三个核心类。

类名 用途简述
Class 代表类的实体,存储着类的完整结构信息(类名、包、字段、方法、构造器等),是反射操作的入口
Constructor 代表类的构造方法。用于在运行时动态创建对象实例,支持调用带参数或私有的构造器。
Method 代表类的成员方法。用于在运行时动态调用指定对象的方法,包括私有方法。
Field 代表类的成员变量(属性)。用于在运行时动态获取或修改对象中的字段值,即使是私有的也可以。

使用层级关系图

Class⭐

.class 文件与 Class 对象的映射关系表

.class 文件 → JVM 类加载 → Class 对象实例化 的底层逻辑链条。

阶段 形态 位置 核心产物 反射的意义
1. 编译期 YourClass.java 硬盘(源码) YourClass.class(字节码指令集) 静态定义,类结构被固化为二进制流
2. 类加载期 字节码二进制流 JVM 方法区(元空间) java.lang.Class 对象实例 将静态文件激活为运行时可操作的内存对象
3. 运行期 Class 对象 + 堆内实例 JVM 堆内存 FieldMethodConstructor 镜像 获得动态改变类属性/动作的能力

Class: 常用获得类相关的方法

方法 用途
getClassLoader() 获得类的加载器
getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有)
forName(String className) 根据类名返回类的对象
newInstance() 创建类的实例
getName() 获得类的完整路径名字
java 复制代码
// 获取Class对象的三种方式
// 获取Class对象
Class<?> clazz = YourClass.class; 
// 通过对象获取Class对象
YourClass obj = new YourClass();
Class<?> clazz = obj.getClass();
//通过Class.forName()动态加载
Class<?> clazz = Class.forName("com.example.YourClass");

// 获取类加载器
ClassLoader classLoader = clazz.getClassLoader();

// 获取所有声明的类和接口
Class<?>[] declaredClasses = clazz.getDeclaredClasses();

// 创建实例
Object instance = clazz.newInstance(); // Java 9+ 已弃用
Object instance = clazz.getDeclaredConstructor().newInstance();

// 获取类名
String className = clazz.getName();

Field:常用获得类中属性相关的方法⭐

方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象
java 复制代码
// 获取公有属性
Field publicField = clazz.getField("publicFieldName");

// 获取所有公有属性
Field[] publicFields = clazz.getFields();

// 获取声明属性(包括私有)
Field declaredField = clazz.getDeclaredField("privateFieldName");

// 获取所有声明属性
Field[] declaredFields = clazz.getDeclaredFields();

// 设置可访问性(用于私有属性)
declaredField.setAccessible(true);

Constructor:获得类中构造方法相关的方法

方法 用途
getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getConstructors() 获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法
java 复制代码
// 获取公有构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);

// 获取所有公有构造方法
Constructor<?>[] constructors = clazz.getConstructors();

// 获取声明构造方法(包括私有)
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();

// 获取所有构造方法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();

// 创建实例
Object instance = constructor.newInstance("参数1", 123);

Method:获得类中方法相关的方法

方法 用途
getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes) 获得该类某个方法
getDeclaredMethods() 获得该类所有方法
java 复制代码
// 获取公有方法
Method method = clazz.getMethod("publicMethodName", String.class, int.class);

// 获取所有公有方法
Method[] methods = clazz.getMethods();

// 获取声明方法(包括私有)
Method declaredMethod = clazz.getDeclaredMethod("privateMethodName");

// 获取所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();

// 调用方法
method.invoke(instance, "参数1", 456);

获得类中注解相关的方法

方法 用途
getAnnotation(Class<T> annotationClass) 返回该类上与参数类型匹配的公有注解对象
getAnnotations() 返回该类上所有的公有注解对象
getDeclaredAnnotation(Class<T> annotationClass) 返回该类上与参数类型匹配的所有注解对象
getDeclaredAnnotations() 返回该类上所有的注解对象
java 复制代码
// 获取特定类型的公有注解
Annotation annotation = clazz.getAnnotation(MyAnnotation.class);

// 获取所有公有注解
Annotation[] annotations = clazz.getAnnotations();

// 获取特定类型的所有注解(包括私有)
Annotation declaredAnnotation = clazz.getDeclaredAnnotation(MyAnnotation.class);

// 获取所有注解(包括私有)
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();

速记口诀

  • Declared全都要(包括私有成员,但只限于当前类声明,不含继承)
  • 不带 Declared只要公有(包括从父类/接口继承而来的公有成员)

反射相关面试笔试题

反射的优缺点是什么?⭐

优点:灵活性强,可扩展性好,适用于框架开发

缺点:性能较低,安全性差,破坏封装性

如何通过反射创建对象?⭐

java 复制代码
Class<?> clazz = Class.forName("com.example.YourClass");
Object obj = clazz.getDeclaredConstructor().newInstance();

如何通过反射调用私有方法?⭐

java 复制代码
Class<?> clazz = Class.forName("com.example.YourClass");
Object obj = clazz.getDeclaredConstructor().newInstance();

Method method = clazz.getDeclaredMethod("privateMethod");
method.setAccessible(true);
method.invoke(obj);

如何通过反射访问私有字段?⭐

java 复制代码
Class<?> clazz = Class.forName("com.example.YourClass");
Object obj = clazz.getDeclaredConstructor().newInstance();

Field field = clazz.getDeclaredField("privateField");
field.setAccessible(true);
Object value = field.get(obj);
field.set(obj, newValue);

反射与new的区别是什么?

  • new在编译时确定类型,反射在运行时确定类型
  • 通过 new 创建对象效率更高。
    原因:反射时需要先查找类资源、通过类加载器加载、进行访问安全检查,过程繁琐,所以效率较低

反射如何处理泛型?

  • 使用getGenericReturnType()等方法获取泛型信息
  • 需要处理Type类型

反射在Spring框架中的应用?

  • IOC容器通过反射创建和管理Bean
  • AOP通过反射实现动态代理

编写一个方法,通过反射获取对象的所有字段名和值

java 复制代码
public static void printFields(Object obj) throws Exception {
    Class<?> clazz = obj.getClass();
    Field[] fields = clazz.getDeclaredFields();
    
    for (Field field : fields) {
        field.setAccessible(true);
        System.out.println(field.getName() + ": " + field.get(obj));
    }
}

编写一个方法,通过反射调用对象的方法

java 复制代码
public static Object invokeMethod(Object obj, String methodName, Object... args) throws Exception {
    Class<?> clazz = obj.getClass();
    Class<?>[] paramTypes = new Class<?>[args.length];
    
    for (int i = 0; i < args.length; i++) {
        paramTypes[i] = args[i].getClass();
    }
    
    Method method = clazz.getMethod(methodName, paramTypes);
    return method.invoke(obj, args);
}
相关推荐
ACGkaka_2 小时前
JDK 版本管理工具介绍:jenv与sdkman(Mac端)
java·macos·sdkman
意法半导体STM322 小时前
【官方原创】STM32 USBx Host HID standardalone移植示例 LAT1449
开发语言·前端·stm32·单片机·嵌入式硬件
阿坤带你走近大数据2 小时前
数据API接口的数据源和目标源分别是什么?怎么设置?
java·python·api
若阳安好2 小时前
【java】任务流批处理平台
java·开发语言
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(六):<线程同步与互斥>线程同步(上)
java·linux·运维·服务器·c++·学习·线程
好家伙VCC2 小时前
# BERT在中文文本分类中的实战优化:从基础模型到高效部署BERT(Bi
java·人工智能·python·分类·bert
chimooing2 小时前
Hermes与OpenClaw的技术碰撞:从JavaScript引擎优化到企业级数据采集的深度解析
开发语言·javascript·ecmascript
身如柳絮随风扬2 小时前
什么是缓存预热
java·spring·缓存
Gofarlic_OMS2 小时前
中小企业控制方法:中小型制造企业Creo许可证成本控制
java·大数据·运维·算法·matlab·制造