【Java】深度解析Java的反射机制

反射(Reflection)

    • [一、 反射的基本概念](#一、 反射的基本概念)
    • [二、 获取类的信息](#二、 获取类的信息)
    • [三、 获取类的成员](#三、 获取类的成员)
    • [四、 动态创建对象](#四、 动态创建对象)
    • [五、 动态调用方法](#五、 动态调用方法)
    • [六、 动态访问和修改字段](#六、 动态访问和修改字段)
  • 总结


一、 反射的基本概念

反射是一种运行时机制,允许程序在运行时检查和操作类、方法、字段等。通过反射,你可以:

  • 获取类的详细信息(类名、修饰符、父类、接口等)。
  • 获取类的方法、构造函数、字段等。
  • 动态调用方法或构造函数。
  • 动态访问和修改字段的值。

二、 获取类的信息

获取 Class 对象

有多种方法可以获取一个类的 Class 对象:

  1. Class.forName(String className): 通过类的完全限定名获取 Class 对象。

  2. ClassName.class: 通过类的字面常量获取 Class 对象。

  3. object.getClass(): 通过对象实例获取 Class 对象。

java 复制代码
// 获取 Class 对象的三种方式
Class<?> clazz1 = Class.forName("java.util.ArrayList");

Class<?> clazz2 = ArrayList.class;
ArrayList<String> list = new ArrayList<>();

 Class<?> clazz3 = list.getClass(); 

三、 获取类的成员

  • getDeclaredFields(): 获取类的所有字段(包括私有字段)。

  • getDeclaredMethods(): 获取类的所有方法(包括私有方法)。

  • getDeclaredConstructors(): 获取类的所有构造函数。

  • getField(String name): 获取类的指定字段(不包括私有字段)。

  • getMethod(String name, Class<?>... parameterTypes): 获取类的指定方法(不包括私有方法)。

  • getConstructor(Class<?>... parameterTypes): 获取类的指定构造函数。

java 复制代码
Class<?> clazz = Class.forName("java.util.ArrayList");
 
// 获取所有声明的字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    System.out.println("字段: " + field.getName());
}
 
// 获取所有声明的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    System.out.println("方法: " + method.getName());
}
 
// 获取所有声明的构造函数
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println("构造函数: " + constructor.getName());
}

四、 动态创建对象

newInstance(): 使用无参构造函数创建对象。

Constructor.newInstance(Object... initargs): 使用指定构造函数创建对象。

java 复制代码
Class<?> clazz = Class.forName("java.util.ArrayList");
 
// 使用无参构造函数创建对象
Object obj1 = clazz.newInstance();
 
// 使用带参数的构造函数创建对象
Constructor<?> constructor = clazz.getConstructor(Collection.class);
Collection<String> collection = Arrays.asList("A", "B", "C");
Object obj2 = constructor.newInstance(collection);
 
System.out.println(obj1);
System.out.println(obj2);

五、 动态调用方法

Method.invoke(Object obj, Object... args): 调用指定对象的该方法。

java 复制代码
Class<?> clazz = Class.forName("java.util.ArrayList");
Method method = clazz.getMethod("add", Object.class);
 
ArrayList<String> list = new ArrayList<>();
method.invoke(list, "Hello");
System.out.println(list); // 输出: [Hello]

六、 动态访问和修改字段

Field.get(Object obj): 获取指定对象中此字段的值。

Field.set(Object obj, Object value): 设置指定对象中此字段的值。

java 复制代码
 
class MyClass {
    private String field = "Initial Value";
}
 
Class<?> clazz = Class.forName("MyClass");
Field field = clazz.getDeclaredField("field");
field.setAccessible(true); // 如果字段是私有的,需要设置可访问
 
MyClass obj = new MyClass();
System.out.println("原始字段值: " + field.get(obj)); // 获取字段值
 
field.set(obj, "New Value"); // 设置字段值
System.out.println("修改后的字段值: " + field.get(obj)); // 获取字段值

结合注解与反射

注解与反射的结合非常常见,尤其在框架中,例如 Spring 和 Hibernate。通过反射机制,你可以在运行时读取注解信息,并根据这些信息执行特定的操作。

示例:简单的依赖注入

以下示例展示了如何通过注解和反射实现简单的依赖注入:

java 复制代码
import java.lang.annotation.*;
import java.lang.reflect.*;
 
// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Inject {
}
 
// 服务类
class Service {
    public void serve() {
        System.out.println("Service is serving");
    }
}
 
// 客户端类
class Client {
    @Inject
    private Service service;
 
    public void doSomething() {
        service.serve();
    }
}
 
// 注入依赖的工具类
public class DependencyInjector {
    public static void main(String[] args) throws Exception {
        Client client = new Client();
        injectDependencies(client);
        client.doSomething();
    }
 
    public static void injectDependencies(Object obj) throws Exception {
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {	// 遍历client的所有字段(变量).
            if (field.isAnnotationPresent(Inject.class)) {	// 获取带有Inject注解的变量, 把它注入到 Client 中
                field.setAccessible(true);
                Object dependency = field.getType().getConstructor().newInstance();
                field.set(obj, dependency);	// 通过field的set方法将service实例注入到client中
            }
        }
    }
}

在这个示例中:

@Inject 注解用于标注需要注入的字段。

DependencyInjector 类通过反射获取 Client 类中带有 @Inject 注解的字段,并动态实例化 Service 类的对象,注入到 Client 类的实例中。

Client 类调用 doSomething 方法时,Service 类的实例已经被注入并可以使用。

总结

反射是 Java 中非常强大和灵活的机制,通过它们可以实现许多高级功能,例如依赖注入、AOP、动态代理等。在实际开发中,理解和熟练运用这些技术,可以帮助你编写出更加灵活、可扩展的代码。

文章到这里就这束了!~

相关推荐
devmoon2 分钟前
为 Pallet 搭建最小化 Mock Runtime 并编写单元测试环境
开发语言·单元测试·区块链·智能合约·polkadot
TT哇5 分钟前
【实习】数字营销系统 银行经理端(interact_bank)前端 Vue 移动端页面的 UI 重构与优化
java·前端·vue.js·ui
喵手9 分钟前
Python爬虫实战:针对Python官网,精准提取出每一个历史版本的版本号、发布日期以及对应的文档/详情页链接等信息,并最终清洗为标准化的CSV文件!
爬虫·python·爬虫实战·零基础python爬虫教学·python官方数据采集·采集历史版本版本号等信息·导出csv文件
Elieal15 分钟前
SpringBoot 数据层开发与企业信息管理系统实战
java·spring boot·后端
识君啊15 分钟前
MyBatis-Plus 逻辑删除导致唯一索引冲突的解决方案
java·spring boot·mybatis·mybatis-plus·唯一索引·逻辑删除
Coder_Boy_17 分钟前
Java开发者破局指南:跳出内卷,借AI赋能,搭建系统化知识体系
java·开发语言·人工智能·spring boot·后端·spring
QT.qtqtqtqtqt18 分钟前
SQL注入漏洞
java·服务器·sql·安全
独自破碎E21 分钟前
BISHI23 小红书推荐系统
java·后端·struts
databook22 分钟前
像搭积木一样思考:数据科学中的“自下而上”之道
python·数据挖掘·数据分析
Mr_Xuhhh22 分钟前
介绍一下ref
开发语言·c++·算法