java反射学习总结

最近在项目上有一个内部的CR,运用到了反射。想起之前面试的时候被面试官追问有没有在项目中用过反射,以及反射的原理和对反射的了解。

于是借此机会,学习回顾一下反射,以及在项目中可能会用到的场景。

 Java 中的反射概述

反射(Reflection) 是 Java 提供的一种机制,它允许程序在运行时动态地检查和操作类的属性、方法以及构造函数等信息。反射使得我们可以在编译时不确定类型的情况下操作对象,比如动态地调用方法、访问属性和创建对象实例。

反射的核心类和接口

反射主要依赖于以下几个类和接口:

  • Class<?>:代表一个类的字节码对象,通过它可以获取类的名称、方法、字段、构造函数等信息。
  • Field:表示类的属性,可以通过它获取或修改某个对象的字段值。
  • Method:表示类的方法,可以通过它调用类的某个方法。
  • Constructor<?>:表示类的构造函数,可以通过它创建类的实例。

反射的主要功能

  • 获取类的结构信息:包括类名、包名、父类、实现的接口等。
  • 获取类的成员信息:包括属性(Field)、方法(Method)、构造函数(Constructor)等。
  • 动态操作对象:包括创建实例、调用方法、修改属性值等。

反射的常用方法

获取类对象 :

Class<?> clazz = Class.forName("com.example.MyClass"); // 通过类的全限定名获取
Class<?> clazz = MyClass.class; // 通过类名.class获取
Class<?> clazz = object.getClass(); // 通过对象实例获取

 获取类的构造函数

Constructor<?> constructor = clazz.getConstructor(); // 获取无参构造函数
Constructor<?> constructor = clazz.getConstructor(String.class); // 获取带参数的构造函数
Object instance = constructor.newInstance("arg"); // 创建类的实例

获取类的方法

Method method = clazz.getMethod("methodName", String.class); // 获取特定名称和参数的方法
method.invoke(instance, "arg"); // 调用方法

 获取类的属性

Field field = clazz.getDeclaredField("fieldName"); // 获取特定名称的属性
field.setAccessible(true); // 设置可访问性,忽略private修饰符
field.set(instance, "value"); // 设置属性值

反射的应用场景

  • 依赖注入(Dependency Injection:如 Spring 框架,通过反射机制在运行时动态地为类的属性赋值,完成依赖注入。
  • 框架设计(如ORM框架:如 Hibernate,通过反射获取实体类的字段和方法,将数据库表和实体类进行映射。
  • 动态代理(Dynamic Proxy:通过反射生成代理类,增强方法的功能,应用于 AOP(面向切面编程)等。
  • 运行时动态操作:在运行时根据配置文件或用户输入动态调用指定的方法,常用于插件化、动态加载等场景。
  • 测试框架(如JUnit:JUnit 等测试框架通过反射来调用测试方法,并通过反射访问私有成员变量以便进行单元测试。

反射的原理

 每个 Java 类在被加载时,JVM 会为该类生成一个唯一的 Class 对象,这个对象包含了类的所有元数据信息,如类名、包名、父类、接口、构造方法、字段、方法等。反射机制的基础就是利用 Class 对象来获取这些信息。

反射的基本操作流程

  • 获取 Class 对象:反射的第一步是获取代表某个类的 Class 对象。可以通过 Class.forName()、类名.class 或 对象.getClass() 来获取。

Class 对象是所有反射操作的基础。JVM 会为每一个被加载的类创建一个 Class 对象,Class 对象中存储了该类的所有元数据信息。

  1. Class.forName(String className):通过类的全限定名在运行时加载类并返回其 Class 对象。
  2. 类名.class:直接通过类名获取该类的 Class 对象。
  3. 对象.getClass():通过对象实例获取其对应的 Class 对象。

这些方法调用后都会返回 JVM 中已经存在的 Class 对象,而不会重新加载类。

  • 获取类的成员信息:通过 Class 对象的各种方法如 getDeclaredMethods()、getDeclaredFields() 等,可以获取类的所有方法、字段、构造函数等信息。

Class 对象中包含了类的所有元数据信息,可以通过以下方法获取:

//获取字段信息(Field):
Constructor<?> constructor = clazz.getDeclaredConstructor(参数类型.class);
//获取方法信息(Method):
Method method = clazz.getDeclaredMethod("methodName", 参数类型.class);
//获取构造函数信息(Constructor):
Field field = clazz.getDeclaredField("fieldName");

Field、Method 和 Constructor 对象中都包含了对应的详细信息(名称、类型、修饰符等),这些信息是 JVM 在类加载时解析字节码并存储在 Class 对象中的。

  • 动态操作:获取到类的结构信息后,可以通过 Method.invoke() 调用方法,通过 Field.set() 或 Field.get() 修改或访问字段,通过 Constructor.newInstance() 创建实例等。

调用方法

Method method = clazz.getDeclaredMethod("methodName", 参数类型.class);
method.setAccessible(true); // 如果方法是私有的,需要设置可访问性
Object result = method.invoke(instance, 参数); // 动态调用方法

反射调用方法的过程大致如下:

  1. 检查方法的可访问性,如果是私有方法则需要设置 setAccessible(true)
  2. 解析方法的参数类型,并匹配传递的参数是否符合要求。
  3. 调用 invoke 时,JVM 通过内部调用找到对应的本地方法实现,然后执行调用。

修改字段值

Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 如果字段是私有的,需要设置可访问性
field.set(instance, value); // 动态修改字段值

修改字段值的过程:

  1. 检查字段的可访问性,如果是私有字段则需要设置 setAccessible(true)
  2. 调用 set() 时,JVM 将新值赋给对象的相应字段。

创建对象实例

Constructor<?> constructor = clazz.getDeclaredConstructor(参数类型.class);
constructor.setAccessible(true); // 如果构造函数是私有的,需要设置可访问性
Object instance = constructor.newInstance(参数); // 动态创建实例

创建对象实例的过程:

  1. 检查构造函数的可访问性,如果是私有构造函数则需要设置 setAccessible(true)
  2. 调用 newInstance() 时,JVM 内部调用本地方法创建对象,并初始化实例。

反射的局限性和问题

  • 性能问题: 反射在运行时解析类的元数据,因此其性能相对直接调用方法要低。频繁使用反射可能导致较大的性能开销。

  • 安全性问题: 反射可以绕过 Java 的访问控制检查,访问和修改类的私有成员。这可能导致安全隐患,特别是在不受信任的环境中。

  • 编译时类型检查缺失: 反射依赖于运行时类型信息,编译器无法对反射代码进行类型检查。这意味着在编译时可能无法检测出反射调用中的错误,只有在运行时才会抛出异常。

面对反射造成的错误该如何解决

  • ClassNotFoundException:类无法找到。

    • 确保类名(包括包名)拼写正确。
    • 确保类在类路径中存在。
  • NoSuchMethodException / NoSuchFieldException:方法或字段不存在。

    • 检查方法名、字段名以及参数类型是否匹配。
    • 确保访问的成员确实存在于目标类中。
  • IllegalAccessException:非法访问异常。

    • 确保 setAccessible(true) 已调用。
    • 确保 JVM 安全管理器允许修改私有成员。
  • InvocationTargetException:目标方法内部抛出异常。

    • 调用 getCause() 获取实际异常,并检查目标方法内部的逻辑。
  • InstantiationException:无法实例化对象。

    • 检查是否试图实例化一个抽象类或接口。
    • 确保存在无参构造函数,或使用带参构造函数。

总结

反射机制是 Java 强大的动态编程功能之一,它允许我们在运行时检查和操作类的结构信息,这在构建灵活的框架和库时非常有用。然而,反射的使用会带来一定的性能和安全性问题,因此在使用时应尽量避免过度使用。理解反射的内部实现原理和应用场景,掌握应对反射相关错误的解决方法,可以更好地利用这一特性来解决实际问题

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

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

相关文章

代码随想录Day17 图论-2

103. 水流问题 本题思路很简单 要求我们找到可以满足到达两个边界的单元格的坐标 有一个优化的思路就是 我们从边界的节点向中间遍历 然后用两个数组表示 一个是第一组边界的数组 一个是第二边界的数组 如果两个数组都遍历到了某一个单元格 就说明该单元格时满足题目要求的 #…

OpenLayers 开源的Web GIS引擎 - 添加地图控件地图控件

中心点按钮、地图放大缩小滑块、全图和比例尺控件 直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.…

如何选择适合的干式电抗器?

干式电抗器是电力系统中重要的电气设备&#xff0c;主要用于限制电网中的短路电流&#xff0c;提高电力系统的稳定性和可靠性。选择适合的干式电抗器对于保障电力系统的正常运行具有重要意义。以下是选择适合的干式电抗器的一些建议&#xff1a; 1. 根据电力系统的需求选择合适…

大模型算法岗常见面试题100道(值得收藏)非常详细收藏我这一篇就够了

大模型应该是目前当之无愧的最有影响力的AI技术&#xff0c;它正在革新各个行业&#xff0c;包括自然语言处理、机器翻译、内容创作和客户服务等等&#xff0c;正在成为未来商业环境的重要组成部分。 截至目前大模型已经超过200个&#xff0c;在大模型纵横的时代&#xff0c;不…

【设计模式】创建型模式(四):建造者模式

《设计模式之创建型模式》系列&#xff0c;共包含以下文章&#xff1a; 创建型模式&#xff08;一&#xff09;&#xff1a;工厂模式创建型模式&#xff08;二&#xff09;&#xff1a;抽象工厂模式创建型模式&#xff08;三&#xff09;&#xff1a;单例模式创建型模式&#…

Android14请求动态申请存储权限

Android14请求动态申请存储权限 Android14和Android15存储权限有增加多了选择部分&#xff0c;还是全部。一个小小的存储权限真的被它玩出了花来。本来Android13就将存储权限进行了3个细分&#xff0c;是图片&#xff0c;音频还是视频文件。 步骤一&#xff1a;AndroidManife…

峟思:山洪灾害监测预警系统全面解析

在自然灾害频发的今天&#xff0c;山洪灾害以其突发性强、破坏力大而备受关注。为了有效预防和减少山洪灾害带来的损失&#xff0c;山洪灾害监测预警系统应运而生。本文将详细介绍该系统的主要组成部分、关键传感器及其工作机制&#xff0c;以期为防灾减灾工作提供有力支持。 山…

项目小总结

这段时间主要把大概的开发流程了解完毕 修改了&#xff0c;并画了几个界面 一.界面 修改为 博客主页 个人中心 二.前后端分离开发 写前端时 就可以假设拿到这些数据了 const blogData2 {blog:{id:1,title: "如何编程飞人",author_id: 1,content: "这是一篇…

最新版C/C++通过CLion2024进行Linux远程开发保姆级教学

目前来说&#xff0c;对Linux远程开发支持相对比较好的也就是Clion和VSCode了&#xff0c;这两个其实对于C和C语言开发都很友好&#xff0c;大可不必过于纠结使用那个&#xff0c;至于VS和QtCreator&#xff0c;前者太过重量级了&#xff0c;后者更是不用说&#xff0c;主要用于…

【论文阅读】Grounding Language with Visual Affordances over Unstructured Data

Abstract 最近的研究表明&#xff0c;大型语言模型&#xff08;llms&#xff09;可以应用于将自然语言应用于各种各样的机器人技能。然而&#xff0c;在实践中&#xff0c;学习多任务、语言条件机器人技能通常需要大规模的数据收集和频繁的人为干预来重置环境或帮助纠正当前的…

vue node node-sass sass-loader 版本 对应 与 兼容

警告&#xff1a; LibSass 和 Node Sass 已弃用。虽然它们将继续无限期地接收维护版本&#xff0c;但没有计划添加其他功能或与任何新的 CSS 或 Sass 功能兼容。仍在使用它的项目应该转移到 Dart Sass。 sass Sass是一种预处理器脚本语言&#xff0c;可以解释或编译成…

Java—反射机制详解

介绍反射 反射的基本概念 反射&#xff08;Reflection&#xff09;是Java语言中的一种机制&#xff0c;它允许程序在运行时检查和操作类、接口、字段和方法等类的内部结构。通过反射&#xff0c;你可以在运行时获取类的信息&#xff0c;包括类的构造器、字段、方法等&#xf…

在 Windows 上运行 Vue 项目时解决 ‘NODE_OPTIONS‘ 错误

在 Windows 上运行 Vue 项目时解决 ‘NODE_OPTIONS’ 错误 在 Windows 系统上启动 Vue 项目时&#xff0c;遭遇报错。具体报错信息如下&#xff1a; ‘NODE_OPTIONS‘ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。这个错误通常意味着 Windows 系统无法识…

机器翻译之创建Seq2Seq的编码器、解码器

1.创建编码器、解码器的基类 1.1创建编码器的基类 from torch import nn#构建编码器的基类 class Encoder(nn.Module): #继承父类nn.Moduledef __init__(self, **kwargs): #**kwargs&#xff1a;不定常的关键字参数super().__init__(**kwargs)def forward(self, X, *args…

基于SpringBoot+Vue+MySQL的美食点餐管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在数字化快速发展的今天&#xff0c;餐饮行业也迎来了转型升级的重要机遇。传统餐饮管理方式面临效率低下、顾客体验不佳等问题。为此&#xff0c;开发一款基于SpringBootVueMySQL架构的美食点餐管理系统显得尤为重要。该系统旨…

【可图(Kolors)部署与使用】大规模文本到图像生成模型部署与使用教程

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 1.Kolors 简介 1.1.什么是Kolors&#xff1f; 开发团队 Kolors 是由快手 Kolors 团队…

网页护眼宝——全方位解析 Chrome Dark Reader 插件

网页护眼宝——全方位解析 Chrome Dark Reader 插件 1. 基本介绍&#xff1a;Chrome 插件的力量与 Dark Reader 的独特之处 随着现代浏览器的功能越来越强大&#xff0c;Chrome 插件为用户提供了极大的定制化能力。从广告屏蔽、性能优化到页面翻译&#xff0c;Chrome 插件几乎…

视频监控相关笔记

一、QT 之 QTreeWidget 树形控件 Qt编程指南&#xff0c;Qt新手教程&#xff0c;Qt Programming Guide 一个树形结构的节点中的图表文本 、附带数据的添加&#xff1a; QTreeWidgetItem* TourTreeWnd::InsertNode(NetNodeInfo node, QTreeWidgetItem* parent_item) { // …

C++: unordered系列关联式容器

目录 1. unordered系列关联式容器1.1 unordered_map1.2 unordered_set 2. 哈希概念3. 哈希冲突4. 闭散列5. 开散列 博客主页: 酷酷学 感谢关注!!! 正文开始 1. unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时…

2024 天池云原生编程挑战赛决赛名单出炉,冠军来自中山大学、昆仑数智战队

9 月 20 日&#xff0c;2024 天池云原生编程挑战赛决赛答辩完美落幕&#xff0c;12 支进入决赛的团队用精彩的答辩&#xff0c;为历时 3 个月的大赛画下了圆满的句号。其中&#xff0c;来自中山大学的陈泓仰以及来自昆仑数智的冉旭欣、沈鑫糠、武鹏鹏&#xff0c; 以出色的方案…