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

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

相关文章:

java反射学习总结

最近在项目上有一个内部的CR&#xff0c;运用到了反射。想起之前面试的时候被面试官追问有没有在项目中用过反射&#xff0c;以及反射的原理和对反射的了解。 于是借此机会&#xff0c;学习回顾一下反射&#xff0c;以及在项目中可能会用到的场景。 Java 中的反射概述 反射&…...

探索C语言与Linux编程:获取当前用户ID与进程ID

探索C语言与Linux编程:获取当前用户ID与进程ID 一、Linux系统概述与用户、进程概念二、C语言与系统调用三、获取当前用户ID四、获取当前进程ID五、综合应用:同时获取用户ID和进程ID六、深入理解与扩展七、结语在操作系统与编程语言的交汇点,Linux作为开源操作系统的典范,为…...

1.4 边界值分析法

欢迎大家订阅【软件测试】 专栏&#xff0c;开启你的软件测试学习之旅&#xff01; 文章目录 前言1 定义2 选取3 具体步骤4 案例分析 本篇文章参考黑马程序员 前言 边界值分析法是一种广泛应用于软件测试中的技术&#xff0c;旨在识别输入值范围内的潜在缺陷。本文将详细探讨…...

Spring IOC容器Bean对象管理-注解方式

目录 1、Bean对象常用注解介绍 2、注解示例说明 1、Bean对象常用注解介绍 Component 通用类组件注解&#xff0c;该类被注解&#xff0c;IOC容器启动时实例化此类对象Controller 注解控制器类Service 注解业务逻辑类Respository 注解和数据库操作的类&#xff0c;如DAO类Reso…...

OpenAI API: How to catch all 5xx errors in Python?

题意&#xff1a;OpenAI API&#xff1a;如何在 Python 中捕获所有 5xx 错误&#xff1f; 问题背景&#xff1a; I want to catch all 5xx errors (e.g., 500) that OpenAI API sends so that I can retry before giving up and reporting an exception. 我想捕获 OpenAI API…...

C++初阶学习——探索STL奥秘——标准库中的priority_queue与模拟实现

1.priority_queque的介绍 1.priority_queue中文叫优先级队列。优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的。 2. 此上下文类似于堆&#xff0c;在堆中可以随时插入元素&#xff0c;并且只能检索最大堆元…...

PyTorch经典模型

PyTorch 经典模型教程 1. PyTorch 库架构概述 PyTorch 是一个广泛使用的深度学习框架&#xff0c;具有高度的灵活性和动态计算图的特性。它支持自动求导功能&#xff0c;并且拥有强大的 GPU 加速能力&#xff0c;适用于各种神经网络模型的训练与部署。 PyTorch 的核心架构包…...

C++ STL容器(三) —— 迭代器底层剖析

本篇聚焦于STL中的迭代器&#xff0c;同样基于MSVC源码。 文章目录 迭代器模式应用场景实现方式优缺点 UML类图代码解析list 迭代器const 迭代器非 const 迭代器 vector 迭代器const 迭代器非const迭代器 反向迭代器 迭代器失效参考资料 迭代器模式 首先迭代器模式是设计模式中…...

力扣416周赛

举报垃圾信息 题目 3295. 举报垃圾信息 - 力扣&#xff08;LeetCode&#xff09; 思路 直接模拟就好了&#xff0c;这题居然是中等难度 代码 public boolean reportSpam(String[] message, String[] bannedWords) {Map<String,Integer> map new HashMap<>()…...

vue 页面常用图表框架

在 Vue.js 页面中&#xff0c;常见的用于制作图表的框架或库有以下几种&#xff1a; ECharts: 官方网站: EChartsECharts 是一个功能强大、可扩展的图表库&#xff0c;支持多种图表类型&#xff0c;如柱状图、折线图、饼图等。Vue 集成: 可以使用 vue-echarts 插件&#xff0c;…...

spring 注解 - @PostConstruct - 用于初始化工作

PostConstruct 是 Java EE 5 中引入的一个注解&#xff0c;用于标注在方法上&#xff0c;表示该方法应该在依赖注入完成之后执行。这个注解是 javax.annotation 包的一部分&#xff0c;通常用于初始化工作&#xff0c;比如初始化成员变量或者启动一些后台任务。 在 Spring 框架…...

多机器学习模型学习

特征处理 import os import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedShuffleSplit from sklearn.impute import SimpleImputer from sklearn.pipeline import FeatureUnion fr…...

【网页设计】前言

本专栏主要记录 “网页设计” 这一课程的相关笔记。 参考资料&#xff1a; 黑马程序员&#xff1a;黑马程序员pink老师前端入门教程&#xff0c;零基础必看的h5(html5)css3移动端前端视频教程_哔哩哔哩_bilibili 教材&#xff1a;《Adobe创意大学 Dreamweaver CS6标准教材》《…...

STM32巡回研讨会总结(2024)

前言 本次ST公司可以说是推出了7大方面&#xff0c;几乎可以说是覆盖到了目前生活中的方方面面&#xff0c;下面总结下我的感受。无线类 支持多种调制模式&#xff08;LoRa、(G)FSK、(G)MSK 和 BPSK&#xff09;满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…...

54 螺旋矩阵

解题思路&#xff1a; \qquad 这道题可以直接用模拟解决&#xff0c;顺时针螺旋可以分解为依次沿“右-下-左-上”四个方向的移动&#xff0c;每次碰到“边界”时改变方向&#xff0c;边界是不可到达或已经到达过的地方&#xff0c;会随着指针移动不断收缩。 vector<int>…...

基于STM32与OpenCV的物料搬运机械臂设计流程

一、项目概述 本文提出了一种新型的物流搬运机器人&#xff0c;旨在提高物流行业的物料搬运效率和准确性。该机器人结合了 PID 闭环控制算法与视觉识别技术&#xff0c;能够在复杂的环境中实现自主巡线与物料识别。 项目目标与用途 目标&#xff1a;设计一款能够自动搬运物流…...

[万字长文]stable diffusion代码阅读笔记

stable diffusion代码阅读笔记 获得更好的阅读体验可以转到我的博客y0k1n0的小破站 本文参考的配置文件信息: AutoencoderKL:stable-diffusion\configs\autoencoder\autoencoder_kl_32x32x4.yaml latent-diffusion:stable-diffusion\configs\latent-diffusion\lsun_churches-ld…...

watchEffect工作原理

watchEffect工作原理 自动依赖收集&#xff1a;watchEffect不需要明确指定要观察的响应式数据&#xff0c;它会自动收集回调函数中用到的所有响应式数据作为依赖。即时执行&#xff1a;watchEffect的回调函数会在组件的setup()函数执行时立即执行一次&#xff0c;以便能够立即…...

斐波那契数列

在 Python 3.11 中实现斐波那契数列的常见方式有多种&#xff0c;下面我将展示几种不同的实现方法&#xff0c;包括递归、迭代和使用缓存&#xff08;动态规划&#xff09;来优化递归版本。 1. 递归方式&#xff08;最简单但效率较低&#xff09; def fibonacci_recursive(n)…...

TCP并发服务器的实现

一请求一线程 问题 当客户端数量较多时&#xff0c;使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。 资源消耗&#xff1a; 线程创建和管理开销&#xff1a;每个线程都有其创建和销毁的开销&#xff0c;特别是在高并发环境中&#xff0c;这种开销…...

前端大屏自适应方案

一般后台管理页面&#xff0c;需要自适应的也就是大屏这一个&#xff0c;其他的尺寸我感觉用第三方框架继承好的就挺合适的&#xff0c;当然自适应方案也可以同步到所有页面&#xff0c;但我感觉除了 to c 的项目&#xff0c;不太需要所有页面自适应&#xff0c;毕竟都是查看和…...

16.3 k8s容器cpu内存告警指标与资源request和limit

本节重点介绍 : Guaranteed的pod Qos最高在生产环境中&#xff0c;如何设置 Kubernetes 的 Limit 和 Request 对于优化应用程序和集群性能至关重要。对于 CPU&#xff0c;如果 pod 中服务使用 CPU 超过设置的limits&#xff0c;pod 不会被 kill 掉但会被限制。如果没有设置 li…...

【计算机网络 - 基础问题】每日 3 题(二十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…...

铰链损失函数

铰链损失函数&#xff08;Hinge Loss&#xff09;主要用于支持向量机&#xff08;SVM&#xff09;中&#xff0c;旨在最大化分类间隔。它的公式为&#xff1a; L ( y , f ( x ) ) max ⁡ ( 0 , 1 − y ⋅ f ( x ) ) L(y, f(x)) \max(0, 1 - y \cdot f(x)) L(y,f(x))max(0,1−…...

项目实战bug修复

实操bug修复记录 左侧侧边栏切换&#xff0c;再次切换侧边栏&#xff0c;右侧未从顶部初始位置展示。地图定位展示&#xff0c;可跳转到设置的对应位置。一个页面多个el-dialog弹出框导致渲染层级出现问题。锚点滚动定位错位问题。动态类名绑定。el-tree树形通过 draggable 属性…...

Git常用指令整理【新手入门级】【by慕羽】

Git 是一个分布式版本控制系统&#xff0c;主要用于跟踪和管理源代码的更改。它允许多名开发者协作&#xff0c;同时提供了强大的功能来管理项目的历史记录和不同版本。本文主要记录和整理&#xff0c;个人理解的Git相关的一些指令和用法 文章目录 一、git安装 & 创建git仓…...

记某学校小程序漏洞挖掘

前言&#xff1a; 遇到一个学校小程序的站点&#xff0c;只在前端登录口做了校验&#xff0c;后端没有任何校验&#xff0c;奇葩弱口令离谱进去&#xff0c;站点里面越权泄露敏感信息&#xff0c;接管账号等漏洞&#xff01;&#xff01;&#xff01; 渗透思路 1.绕过前端 …...

腾讯百度阿里华为常见算法面试题TOP100(3):链表、栈、特殊技巧

之前总结过字节跳动TOP50算法面试题: 字节跳动常见算法面试题top50整理_沉迷单车的追风少年-CSDN博客_字节算法面试题 链表 160.相交链表...

Apache CVE-2021-41773 漏洞复现

1.打开环境 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2.访问靶场 3.使用poc curl http://47.121.191.208:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 4.工具验证...

vue-入门速通

setup是最早的生命周期&#xff0c;在vue2里边的data域可以使用this调用setup里面的数据&#xff0c;但是在setup里边不能使用thisvue项目的可执行文件是index&#xff0c;另外运行前端需要npm run vue的三个模块内需要三个不同的结构&#xff0c;里边放置js代码&#xff0c;注…...