当前位置: 首页 > news >正文

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

反射(Reflection)

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

在这里插入图片描述
在这里插入图片描述

一、 反射的基本概念

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

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

二、 获取类的信息

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

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

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

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

// 获取 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): 获取类的指定构造函数。

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): 使用指定构造函数创建对象。

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): 调用指定对象的该方法。

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): 设置指定对象中此字段的值。

 
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。通过反射机制,你可以在运行时读取注解信息,并根据这些信息执行特定的操作。

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

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 {@Injectprivate 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、动态代理等。在实际开发中,理解和熟练运用这些技术,可以帮助你编写出更加灵活、可扩展的代码。

文章到这里就这束了!~

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 后端学习笔记(3)--Maven
  • python爬虫实践
  • 【C++】入门基础知识
  • 用VBA在Word中随机打乱单词表,进行分列
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • 数据守护者的秘籍:SQL Server数据库备份验证全攻略
  • python实现小游戏——植物大战僵尸(魔改版本)
  • Apache Kylin入门指南
  • 链表篇: 04-寻找两个链表的第一个公共结点
  • [极客大挑战 2019]BuyFlag1
  • A+B V2 51Nod - 3415
  • 实验4-1-7 特殊a串数列求和
  • python 中的 join()
  • 【第二章】软件开发生命周期-瀑布模型:详细解析与案例分析
  • python使用venv生成虚拟环境
  • php的引用
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 【知识碎片】第三方登录弹窗效果
  • 30天自制操作系统-2
  • Date型的使用
  • vagrant 添加本地 box 安装 laravel homestead
  • Vue 2.3、2.4 知识点小结
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 技术:超级实用的电脑小技巧
  • 前端_面试
  • 容器服务kubernetes弹性伸缩高级用法
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 湖北分布式智能数据采集方法有哪些?
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #微信小程序(布局、渲染层基础知识)
  • (bean配置类的注解开发)学习Spring的第十三天
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (vue)el-tabs选中最后一项后更新数据后无法展开
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (转)jQuery 基础
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET Core 版本不支持的问题
  • .net dataexcel winform控件 更新 日志
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • /bin/bash^M: bad interpreter: No such file ordirectory
  • @ConfigurationProperties注解对数据的自动封装
  • @ohos.systemParameterEnhance系统参数接口调用:控制设备硬件(执行shell命令方式)
  • @Valid和@NotNull字段校验使用
  • [ CTF ] WriteUp-2022年春秋杯网络安全联赛-冬季赛
  • [AIGC] MySQL存储引擎详解
  • [Android 13]Input系列--获取触摸窗口
  • [Android]RecyclerView添加HeaderView出现宽度问题
  • [BZOJ4554][TJOI2016HEOI2016]游戏(匈牙利)
  • [C#]winform制作仪表盘好用的表盘控件和使用方法