Java反射Reflect机制详解

文章目录

  • 引言
  • 反射的基本概念
  • 反射基本原理
  • 反射应用场景
  • 反射基本使用
    • 获取类的Class对象
    • 获取构造方法并实例化对象
    • 获取和调用方法
    • 获取和修改字段
    • 反射工具类
  • 反射源码解读
    • 获取Class对象的源码
    • 调用方法的源码
  • 反射优缺点
    • 优点
    • 缺点
  • 为什么需要反射
  • 总结

引言

Java反射是Java语言中的一种动态机制,它允许在运行时检查和操作类的结构和行为。反射的强大功能使得程序可以在运行时动态加载类、调用方法和访问字段,从而极大地增强了Java程序的灵活性和扩展性。

反射的基本概念

反射(Reflection)是指程序在运行时能够自我检查和操作自身的能力。通过反射,可以获取类的构造器、方法、字段等信息,并能动态调用对象的方法、设置或获取对象的字段值。

反射关键信息

  • Class: 代表类的实体,在运行时加载类时会创建对应的Class对象。
  • Constructor: 代表类的构造方法。
  • Method: 代表类的方法。
  • Field: 代表类的字段。

Java反射最核心的类位于JDK源码 java.lang.reflect包下,比如Class、Constructor、Field 和 Method等,他们提供了对类和对象运行时信息进行检查和操作的方法。

Java反射Reflect机制详解 - Java技术债务

反射基本原理

Java反射的核心在于Class类,它包含了关于类的所有信息。在Java虚拟机(JVM)加载类时,会为每个类创建一个对应的Class对象,该对象保存了类的元数据。通过这些元数据,程序可以在运行时获取类的详细信息并进行操作。

主要可以从下面 4个点来阐述:

  1. 类加载:当 Java程序运行时,类加载器会根据类的名称查找并加载类的字节码文件,然后将字节码文件转换为可执行的 Java类,并将其存储在运行时数据区域的方法区中。
  2. 创建 Class对象:在类加载过程中,Java虚拟机会自动创建对应的Class对象,Class对象包含了类的元数据信息,并提供了访问和操作类的接口。
  3. 获取 Class对象:Class对象通过多种方式获取,最常见的方式有 3种: 类的 .class属性、类实例的 getClass()方法、Class.forName()。
  4. 访问和操作:通过Class对象获取类的字段、方法、构造函数等信息,使用Field类和Method类来访问和操作字段和方法,甚至可以调用私有的字段和方法。

通过上述的分析可以看出:反射机制需要基于Java虚拟机对类的加载、存储和访问机制的支持,通过反射,可以在运行时动态地探索和操作类的信息,实现灵活的编程和代码的动态行为。

反射应用场景

很多优秀的框架内部都使用了Java反射,这里重点讲解下给 Java打下半壁江山的 Spring生态(Spring Framework,Spring MVC,SpringBoot, SpringCloud…),以 Spring Framework为例:

  1. 依赖注入(Dependency Injection) : 依赖注入,可以把程序员主动创建对象的事情交给 Spring管理,大大提升了对象创建的灵活性。当我们在配置文件或用注解定义 Bean时,Spring会使用反射来动态地实例化对象,并将依赖的其他对象注入到这些实例中。
  2. 自动装配(Autowired) : 当 Spring容器启动时,它会扫描应用程序中的所有类,并使用反射来查找和识别带有 @Autowired注解的字段、方法或构造函数。再自动将 Bean注入到需要的位置,实现对象之间的自动连接。
  3. AOP(Aspect-Oriented Programming) : AOP 利用了动态代理和反射机制。通过定义切面(Aspect)和切点(Pointcut),Spring可以在运行时使用反射来创建代理对象,从而实现横切关注点(cross-cutting concerns)的功能,如日志记录、事务管理等。
  4. 动态代理(Dynamic Proxy) : Spring利用 Java反射机制动态地创建代理对象,并在代理对象中添加额外的逻辑,从而实现对目标对象的增强。
  5. 框架扩展和定制: Spring通过反射机制来实现对应用程序的扩展和定制的。例如,Spring提供了BeanPostProcessor接口,允许开发人员在 Bean初始化前后插入自定义逻辑,这是通过反射来实现的。

另外,还有一些耳熟能详的框架也使用了Java反射

  1. JUnit:JUnit是一个优秀的单元测试框架,它利用了 Java反射机制动态地加载和执行测试方法。
  2. Jackson:Jackson是一个 JSON处理的 Java库,它利用反射来实现 JSON与 Java对象之间的转换,动态读取和写入 Java对象的属性,并将其转换为 JSON格式。
  3. Hibernate ORM:Hibernate和 MyBatis一样,都是对象关系映射框架,通过反射来实现对象与数据库表之间的映射关系。

总结以下几点:

  • 框架设计: 许多Java框架(如Spring、Hibernate)广泛使用反射来实现依赖注入、面向切面编程等功能。
  • 调试和测试: 反射允许动态访问和修改对象,方便调试和测试私有方法和字段。
  • 动态代理: 通过反射实现动态代理,增强程序的灵活性和可扩展性。
  • 类浏览器和可视化工具: 反射帮助开发工具展示类的结构和关系。

反射基本使用

获取类的Class对象

Class<?> clazz = Class.forName("com.example.MyClass");
// 或者
Class<?> clazz = MyClass.class;
// 或者
Class<?> clazz = myObject.getClass();

获取构造方法并实例化对象

Constructor<?> constructor = clazz.getConstructor(String.class);
Object instance = constructor.newInstance("example");

获取和调用方法

Method method = clazz.getMethod("myMethod", String.class);
method.invoke(instance, "Hello");

获取和修改字段

Field field = clazz.getDeclaredField("myField");
// 允许访问私有字段
field.setAccessible(true); 
field.set(instance, "New Value");
String value = (String) field.get(instance);

反射工具类

@Slf4j
public class ReflectionUtil {/*** 获取属性名以及对应的属性值** @param o 对象* @return map*/public static Map<String, Object> getFieldNameAndValue(Object o) {Map<String, Object> resMap = new LinkedHashMap<>();Class<?> clazz = o.getClass();while (Objects.nonNull(clazz)) {Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {String fieldName = field.getName();if (Objects.isNull(resMap.get(fieldName))) {resMap.put(fieldName, getFieldValueByName(fieldName, o));}}clazz = clazz.getSuperclass();}return resMap;}/*** 获取属性名以及对应的属性值** @param list 对象数组* @return list\<map\>*/public static List<Map<String, Object>> getFieldNameAndValueMaps(List<?> list) {List<Map<String, Object>> resMaps = new ArrayList<>(list.size());for (Object o : list) {if (Objects.isNull(o)) {throw new RuntimeException("Arrays Cannot Contain Null... ...");}Map<String, Object> fieldNameAndValueMap = getFieldNameAndValue(o);resMaps.add(fieldNameAndValueMap);}return resMaps;}public static List<String> getFieldNames(Class<?> clazz) {List<String> fieldList = new ArrayList<>();while (Objects.nonNull(clazz)) {Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {String fieldName = field.getName();fieldList.add(fieldName);}clazz = clazz.getSuperclass();}return fieldList;}/*** 获取类上的指定字段。如果在类本身上找不到该字段,则将递归检查超类。** @param clazz     source class* @param fieldName 字段名* @return 字段*/private static Field getField(Class<?> clazz, String fieldName) {try {return clazz.getDeclaredField(fieldName);} catch (NoSuchFieldException nsf) {if (clazz.getSuperclass() != null) {return getField(clazz.getSuperclass(), fieldName);}throw new IllegalStateException("Could not locate field '" + fieldName + "' on class " + clazz);}}/*** 根据属性名获取属性值** @param fieldName 属性名称* @param o         对象* @return Object*/public static Object getFieldValueByName(String fieldName, Object o) {try {String firstLetter = fieldName.substring(0, 1).toUpperCase();String getter = "get" + firstLetter + fieldName.substring(1);Method method = o.getClass().getMethod(getter);return method.invoke(o);} catch (Exception e) {log.info("根据属性名获取属性值异常:" + e.getMessage() + "\n" + e);return null;}}@SuppressWarnings({"all"})public static void setFiledValue(Object bean, String filedName, Object value) throws NoSuchFieldException, IllegalAccessException {Class<?> aClass = bean.getClass();Field field = getField(aClass, filedName);field.setAccessible(true);field.set(bean, value);}
}

反射源码解读

反射的实现依赖于JVM提供的本地方法接口(JNI),通过调用本地方法实现对类信息的获取和操作。

获取Class对象的源码

public static Class<?> forName(String className) throws ClassNotFoundException {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();if (classLoader == null) {classLoader = ClassLoader.getSystemClassLoader();}return classLoader.loadClass(className);
}

调用方法的源码

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class<?> caller = Reflection.getCallerClass();checkAccess(caller, clazz, obj, modifiers);}}MethodAccessor ma = methodAccessor;             // read volatileif (ma == null) {ma = acquireMethodAccessor();}return ma.invoke(obj, args);
}

从源码可以看出:Method.invoke()方法,真实返回的是接口MethodAccessor.invoke()方法。MethodAccessor接口有三个实现类,具体是调用哪个类的 invoke 方法?

Java反射Reflect机制详解 - Java技术债务

跟到源码最后可以发现:Method.invoke()方法最终调用 native的invoke0(),应用层面的操作最终转换成对操作系统 c/c++方法的调用。

反射优缺点

优点

  • 灵活性: 反射允许在运行时动态操作类,提高了程序的灵活性和扩展性。
  • 动态代理: 通过反射可以实现动态代理机制,广泛应用于AOP(面向切面编程)等领域。
  • 通用性: 反射可以用来编写通用的框架和库,增强代码的重用性。

缺点

  • 性能开销: 反射操作较为耗时,可能会影响程序性能。
  • 安全问题: 反射可以绕过访问控制,修改私有字段和方法,可能引发安全问题。
  • 代码复杂性: 使用反射可能增加代码的复杂性和维护难度。

为什么需要反射

反射机制在 Java中的作用不言而喻,下面列举了反射机制的一些常见场景和原因:

  1. 运行时类型检查:反射机制允许在运行时获取类的信息,包括字段、方法和构造方法等。因此,在进行运行时类型检查,以确保代码在处理不同类型的对象时能够正确地进行操作。
  2. 动态创建对象:通过反射,可以在运行时动态地创建对象,而不需要在编译时知道具体的类名。这对于某些需要根据条件或配置来创建对象的情况非常有用,例如工厂模式或依赖注入框架。
  3. 访问和修改私有成员:反射机制可以绕过访问权限限制,访问和修改类的私有字段和方法。虽然这破坏了封装性原则,但在某些特定情况下,这种能力可以帮助我们进行一些特殊操作,例如单元测试、调试或框架的内部实现。
  4. 动态调用方法:反射机制允许我们在运行时动态地调用类的方法,甚至可以根据运行时的条件来选择不同的方法。这对于实现插件化系统、处理回调函数或实现动态代理等功能非常有用。
  5. 框架和库的实现:许多Java框架和库在其实现中广泛使用了反射机制。它们利用反射来自动发现和加载类、实现依赖注入、处理注解、配置文件解析和动态代理等。反射机制使得这些框架和库更加灵活和扩展。

总结

Java反射是一个强大的工具,极大地增强了Java语言的动态性和灵活性。然而,在使用反射时需要权衡其性能开销和安全风险。Java反射有优点也有缺点,从整体上看,Java反射是以牺牲了小部分的性能换取了更好的扩展性和灵活性,牺牲小我成就大我,而且,随着现代硬件设备能力越来越强,这点小性能的牺牲是完全值得的。理解反射的原理和使用场景,可以更好地应用反射技术来解决实际开发中的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/23636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【干货】视频文件抽帧(opencv和ffmpeg方式对比)

1 废话不多说&#xff0c;直接上代码 opencv方式 import time import subprocess import cv2, os from math import ceildef extract_frames_opencv(video_path, output_folder, frame_rate1):"""使用 OpenCV 从视频中抽取每秒指定帧数的帧,并保存到指定文件夹…

宝贝,带上WebAssembly,换个姿势来优化你的前端应用

在你没崛起之前,脸是用来丢的 大家好,我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder 此篇文章所涉及到的技术有 WebAssemblyRustWeb Worker(comlink)wasm-packPhotonffmpeg.wasm脚手架生成前端项目因为,行文字数所限,有些概念可能会一带而过亦或者提供对…

经济与安全兼顾:茶饮店购买可燃气体报警器的价格考量

可燃气体报警器在如今的社会中扮演着至关重要的角色。它们用于检测环境中的可燃气体浓度&#xff0c;及早发现潜在的火灾隐患&#xff0c;保护人们的生命和财产安全。 在这篇文章中&#xff0c;佰德将介绍可燃气体报警器的安装、检定以及价格&#xff0c;通过实际案例和数据&a…

PCL 生成空间椭圆点云

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 设椭圆在 X O Y XOY XOY平面上,参数方程为:

牛啊后续:如何一行C#代码实现解析类型的Summary注释(可用于数据字典快速生成)...

前言&#xff1a;下午有小伙伴要求&#xff0c;让我继续做个解析实体类注释信息的内容。所以我也顺便加入进来。以下开始正文实战操作&#xff1a; 项目需要勾选输出api文档文件。这样就可以让所有实体类的summary信息被写入到输出目录下。如果有多个xml文件也没关系&#xff0…

小程序 UI 风格美不胜收

小程序 UI 风格美不胜收 小程序 UI 风格美不胜收

MyEclipse中properties文件中文乱码(Unicode字符)解决办法

程序代码园发文地址&#xff1a;MyEclipse中properties文件中文乱码&#xff08;Unicode字符&#xff09;解决办法-程序代码园小说,Java,HTML,Java小工具,程序代码园,http://www.byqws.com/ ,MyEclipse中properties文件中文乱码&#xff08;Unicode字符&#xff09;解决办法htt…

Django学习三:views业务层中通过models对实体对象进行的增、删、改、查操作。

文章目录 前言一、Django ORM介绍二、项目快速搭建三、操作1、view.pya、增加操作b、删除操作c、修改操作d、查询操作 2、urls.py 前言 上接博文&#xff1a;Django学习二&#xff1a;配置mysql&#xff0c;创建model实例&#xff0c;自动创建数据库表&#xff0c;对mysql数据…

一周发文9篇!MIMIC-IV数据库周报(5.22~5.28)

重症医学数据库&#xff08;MIMIC&#xff09;是由计算生理学实验室开发的公开数据集&#xff0c;其中包括与数千个重症监护病房入院相关的去识别化健康数据&#xff0c;致力于推动临床信息学、流行病学和机器学习的研究。 MIMIC数据库于2003年在美国国立卫生研究院的资助下&am…

2024上海初中生古诗文大会倒计时4个半月:单选题真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个半月时间&#xff0c;我们来看10道选择题真题和详细解析&#xff0c;了解古诗文大会的考察方式和知识点&#xff0c;从而更好地备考。 以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&am…

面试成功的不二法门:详解Vue3答题章法

前言 面试题在网络上有如海洋之深&#xff0c;对于同一知识点&#xff0c;每个人的理解也各有千秋。我们在面试中常常会遇到一个瞬息间脑海里一片空白的情况&#xff0c;其实这并不是因为我们不懂&#xff0c;而是因为我们在回答的时候缺乏一个清晰的思路。那么问题来了&#x…

《魔法与科技的融合:SpringBoot运维的现代传说》

揭开了SpringBoot应用部署的神秘面纱。从云平台的选型到Docker的容器化魔法&#xff0c;再到Kubernetes的集群力量&#xff0c;每一步都充满了奇幻色彩。文章以轻松幽默的笔触&#xff0c;带领读者穿梭于现代应用部署的各个角落&#xff0c;探索自动化部署的奥秘&#xff0c;学…

【深度学习】PuLID: Pure and Lightning ID Customization via Contrastive Alignment

论文&#xff1a;https://arxiv.org/abs/2404.16022 代码&#xff1a;https://github.com/ToTheBeginning/PuLID 文章目录 AbstractIntroductionRelated WorkMethods Abstract 我们提出了一种新颖的、无需调整的文本生成图像ID定制方法——Pure and Lightning ID customizatio…

微信好友朋友圈的三天、半年可见怎么破?方法拿走不谢

『Code掘金』问大家&#xff0c;有没有这种经历&#xff0c;当你想去翻某人的朋友圈时&#xff0c;对方设置成了3天可见&#xff0c;之前的内容没法看到了。 不过没关系&#xff01;今天『Code掘金』给大家分享一款导出朋友圈的工具&#xff0c;让大家留住痕迹。 WechatMomen…

初探沁恒CH32V307VCT6评估板 1-1外部中断EXTI

旋转编码器计次 1&#xff0c;创建 .C .H 文件 Count_Sonser.c Count_Sonser.h 2&#xff0c;包含头文件 #include "ch32v30x.h" //#include "debug.h" #include "Count_Sonser.h" 注意&#xff1a;如果不包含第一第二个头文件uint、int这…

如何掌握 Java 正则表达式 的基本语法及在 Java 中的应用

正则表达式是一种用于匹配字符串的模式&#xff0c;在许多编程语言中广泛使用。Java 正则表达式提供了强大的文本处理能力&#xff0c;能够对字符串进行查找、替换、分割等操作。 一、正则表达式的基本语法 正则表达式由普通字符和特殊字符组成。普通字符包括字母、数字和标点…

网络协议三

数据中心 一、DNS 现在网站的数目非常多&#xff0c;常用的网站就有二三十个&#xff0c;如果全部用 IP 地址进行访问&#xff0c;恐怕很难记住 根 DNS 服务器 &#xff1a;返回顶级域 DNS 服务器的 IP 地址 顶级域 DNS 服务器&#xff1a;返回权威 DNS 服务器的 IP 地址 …

汇凯金业:贵金属交易规则有哪些

贵金属投资目前非常火热&#xff0c;许多投资者从中获得了可观的收益。新手投资者在进入贵金属市场及其交易之前&#xff0c;务必要了解清楚贵金属的交易规则。了解规则和差异能帮助新手更好地进行贵金属投资交易。下面我们以现货类贵金属为例&#xff0c;详细说明贵金属的交易…

一周学会Django5 Python Web开发 - Django5内置Auth认证系统-用户修改密码实现

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计59条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

基于springboot的-仓库 管理系统【附:资料➕文档】

前言&#xff1a;我是源码分享交流Coding&#xff0c;专注JavaVue领域&#xff0c;专业提供程序设计开发、源码分享、 技术指导讲解、各类项目免费分享&#xff0c;定制和毕业设计服务&#xff01; 免费获取方式--->>文章末尾处&#xff01; 项目介绍&#xff1a; 管理员…