Java-反射机制

在 Java 编程中,“反射” 是一个贯穿基础与进阶的核心概念,它允许程序在运行时动态获取类的结构、调用方法、操作属性,甚至创建对象 —— 无需在编译期明确知道类的具体信息。

一、反射是什么?

首先明确一个关键定义:Java 反射(Reflection)是 Java 语言提供的一种能力,允许程序在运行时(而非编译时)访问、检测和修改类、对象、方法、属性等程序元素的信息

反射的核心价值

  • 动态性:打破编译期的固定依赖,运行时灵活操作类结构(比如根据配置文件加载不同类)。
  • 通用性:可编写通用框架(如 Spring、MyBatis),通过反射适配任意类,无需为每个类单独写代码。
  • 穿透性:支持 “暴力反射”,可突破类成员的访问权限限制(如操作 private 私有属性 / 方法)。

二、反射的前提:获取 “类对象”

反射的所有操作,都必须基于一个核心载体 ——Class 类的对象(简称 “类对象”)。每个类在 JVM 中只会被加载一次,因此同一个类的类对象全局唯一。

获取类对象的 3 种核心方式

这 3 种方式对应类的 “生命周期”(硬盘→内存→对象)整理如下:

获取方式语法示例适用场景关键说明
1. Class.forName (全类名)Class pClass = Class.forName("reflect.Person");编译期未知类名,需动态指定(如读配置文件)需传入 “包名 + 类名”,会触发类的加载,可能抛出 ClassNotFoundException
2. 类名.classClass pClass = Person.class;编译期已知类名,需静态获取不会触发类的加载,仅获取类的静态结构信息
3. 对象.getClass ()Person person = new Person(); Class pClass = person.getClass();已有对象实例,需通过对象反推类信息依赖具体对象,适用于 “已知对象但未知类” 的场景

验证唯一性:同一个类的 3 种方式获取的类对象地址完全相同

Class class1 = Class.forName("reflect.Person");
Class class2 = Person.class;
Person person = new Person();
Class class3 = person.getClass();// 输出结果均为 true,证明类对象唯一
System.out.println(class1 == class2); 
System.out.println(class2 == class3);

三、反射核心操作:全方位操控类结构

获取类对象后,即可通过反射 API 操作类的三大核心组件:成员变量、成员方法、构造方法。以下结合 Test.java 和 Person.java 的实践代码,分模块梳理。

模块 1:操作 “成员变量”(Field)

成员变量的反射操作,核心是 “获取变量” 和 “读写变量值”,需区分 “所有权限” 和 “仅公共权限(public)”,同时支持暴力反射突破私有限制。

1.1 获取成员变量的 4 个核心方法
方法名作用访问权限范围是否包含父类变量
getDeclaredFields()获取当前类的所有成员变量任意权限(public/private/protected)不包含
getDeclaredField(String name)获取当前类的指定名称成员变量任意权限不包含
getFields()获取当前类的所有公共成员变量仅 public包含父类的 public 变量
1.2 读写变量值:set () 与 get ()
  • 语法
    • 写值:field.set(对象实例, 变量值)(为指定对象的该变量赋值)
    • 读值:field.get(对象实例)(获取指定对象的该变量值)
  • 关键注意:若变量是 private 私有权限,直接读写会抛出 IllegalAccessException,需先调用 field.setAccessible(true) 开启 “暴力反射”,强制跳过权限检查。
1.3 实战代码
// 1. 创建 Person 对象实例
Person person = new Person();
// 2. 获取类对象
Class pClass = Class.forName("reflect.Person");// 3. 获取所有成员变量(含 private)
Field[] allFields = pClass.getDeclaredFields();
for (Field field : allFields) {System.out.println(field); // 输出:private java.lang.String reflect.Person.name、private int reflect.Person.age、public java.lang.String reflect.Person.from
}// 4. 获取指定私有变量 name(需暴力反射)
Field nameField = pClass.getDeclaredField("name");
nameField.setAccessible(true); // 开启暴力反射,突破 private 限制
nameField.set(person, "赵嘉成"); // 赋值
System.out.println(nameField.get(person)); // 取值,输出:赵嘉成// 5. 获取指定公共变量 from(无需暴力反射)
Field fromField = pClass.getDeclaredField("from");
fromField.set(person, "中国"); // 直接赋值
System.out.println(person); // 输出:Person [name=赵嘉成, age=0, from=中国]

模块 2:操作 “成员方法”(Method)

成员方法的反射操作,核心是 “获取方法” 和 “执行方法”,同样需区分权限范围,支持暴力反射调用私有方法。

2.1 获取成员方法的 4 个核心方法
方法名作用访问权限范围是否包含父类方法
getDeclaredMethods()获取当前类的所有成员方法任意权限不包含
getDeclaredMethod(String name, Class<?>... paramTypes)获取当前类的指定名称和参数列表的方法任意权限不包含
getMethods()获取当前类的所有公共成员方法仅 public包含父类的 public 方法(如 Object 的 toString ())
getMethod(String name, Class<?>... paramTypes)获取当前类的指定名称和参数列表的公共方法仅 public包含父类的 public 方法
2.2 执行方法:invoke ()

语法:method.invoke(对象实例, 方法参数值...)

  • 若方法是静态方法(static),对象实例可传 null
  • 若方法无参数,参数值部分可省略或传空数组;
  • 若方法有返回值,invoke() 会返回该值(需强转)。

权限注意:私有方法需先调用 method.setAccessible(true) 开启暴力反射。

2.3 实战代码
// 1. 获取类对象(省略,同上)
Class pClass = Class.forName("reflect.Person");
Person person = new Person();// 2. 获取所有方法(含 private 的 run())
Method[] allMethods = pClass.getDeclaredMethods();
for (Method method : allMethods) {System.out.println(method); // 输出:getAge()、setAge(int)、run() 等
}// 3. 获取指定私有方法 run()(无参数)
Method runMethod = pClass.getDeclaredMethod("run");
runMethod.setAccessible(true); // 暴力反射突破 private
runMethod.invoke(person); // 执行 run() 方法(无返回值)// 4. 获取指定公共方法 getAge()(无参数,有返回值)
Method getAgeMethod = pClass.getMethod("getAge");
int age = (int) getAgeMethod.invoke(person); // 执行并接收返回值
System.out.println(age); // 输出:0(Person 初始 age 为 0)

模块 3:操作 “构造方法”(Constructor)

构造方法的反射操作,核心是 “获取构造器” 和 “创建对象”(替代 new 关键字),支持通过无参 / 有参构造器创建实例。

3.1 获取构造方法的 4 个核心方法
方法名作用访问权限范围是否包含父类构造器
getDeclaredConstructors()获取当前类的所有构造方法任意权限不包含(构造器不能继承)
getDeclaredConstructor(Class<?>... paramTypes)获取当前类的指定参数列表的构造方法任意权限不包含
getConstructors()获取当前类的所有公共构造方法仅 public不包含
getConstructor(Class<?>... paramTypes)获取当前类的指定参数列表的公共构造方法仅 public

不包含

3.2 创建对象:newInstance ()
  • 语法constructor.newInstance(构造参数值...)
    • 无参构造器:参数值部分可省略,直接 constructor.newInstance()
    • 有参构造器:需传入与参数列表匹配的参数值;
    • 私有构造器:需先调用 constructor.setAccessible(true) 开启暴力反射。
3.3 实战代码
// 1. 获取类对象(省略)
Class pClass = Class.forName("reflect.Person");// 2. 获取所有构造器(Person 有 3 个:无参、String name、String name+int age)
Constructor[] allConstructors = pClass.getDeclaredConstructors();
for (Constructor constructor : allConstructors) {System.out.println(constructor); // 输出:Person()、Person(java.lang.String)、Person(java.lang.String,int)
}// 3. 获取无参公共构造器,创建对象
Constructor noArgConstructor = pClass.getConstructor();
Person person1 = (Person) noArgConstructor.newInstance();
System.out.println(person1); // 输出:Person [name=null, age=0, from=null]// 4. 获取有参公共构造器(String name + int age),创建对象
Constructor twoArgConstructor = pClass.getConstructor(String.class, int.class);
Person person2 = (Person) twoArgConstructor.newInstance("丽丽", 20);
System.out.println(person2); // 输出:Person [name=丽丽, age=20, from=null]

四、反射实战:结合配置文件实现 “动态加载”

反射的核心优势是 “动态性”,而结合配置文件(如 .properties)可实现 “不修改代码,仅改配置就能切换类 / 方法”。Test.java 中已实现该场景,整理如下:

需求场景

通过 refconfig.properties 配置文件指定类名和方法名,运行时动态加载类并调用方法,无需硬编码类名。

步骤拆解与代码

创建配置文件(refconfig.properties):

# 配置要加载的类(全类名)
reflect.className=reflect.Person
# 配置要调用的方法名
reflect.methodName=getAge

读取配置文件并动态反射:

// 1. 创建 Properties 对象,用于读取配置文件
Properties props = new Properties();// 2. 通过类加载器加载配置文件(注意路径:src/main/resources/reflect/refconfig.properties)
InputStream in = Person.class.getClassLoader().getResourceAsStream("reflect/refconfig.properties");
props.load(in); // 读取配置内容// 3. 从配置中获取类名和方法名
String className = props.getProperty("reflect.className");
String methodName = props.getProperty("reflect.methodName");// 4. 动态加载类,获取类对象
Class dynamicClass = Class.forName(className);
System.out.println(dynamicClass); // 输出:class reflect.Person// 5. 动态获取方法并执行(这里以无参方法 getAge() 为例)
Method dynamicMethod = dynamicClass.getMethod(methodName);
Object instance = dynamicClass.getConstructor().newInstance(); // 创建对象
dynamicMethod.invoke(instance); // 执行方法,输出:getAge方法执行

核心价值

若后续需要切换为操作 Cat.java(而非 Person.java),只需修改配置文件的 reflect.className=reflect.Cat,无需修改 Java 代码 —— 这正是框架(如 Spring)“解耦” 的核心原理。

五、反射关键补充:类信息获取与注意事项

5.1 获取类的基本信息

通过类对象可快速获取类的名称、包名等信息,Test.java 中已实践:

方法名作用示例(Person 类)
getName()获取全类名(包名 + 类名)pClass.getName() → "reflect.Person"
getSimpleName()获取简单类名(仅类名)

pClass.getSimpleName() → "Person"

5.2 反射的注意事项

反射虽强大,但存在以下问题,使用时需谨慎:

  1. 性能开销:反射操作需在运行时解析类结构,比直接调用(如 person.getAge())慢,高频场景(如循环中)需避免。
  2. 安全风险:暴力反射突破了访问权限限制,可能破坏类的封装性(如修改私有变量),需确保操作的合理性。
  3. 代码可读性:反射代码较抽象,不如直接调用直观,需添加清晰注释。
  4. 兼容性风险:若类结构修改(如方法名、参数列表变更),反射代码可能抛出 NoSuchMethodException 等异常,需做好异常处理。

六、总结:反射知识体系图谱

最后,用一张图谱梳理本文核心内容,方便复习回顾:

Java 反射
├─ 核心前提:获取类对象(3种方式)
│  ├─ Class.forName(全类名) → 动态加载
│  ├─ 类名.class → 静态获取
│  └─ 对象.getClass() → 实例反推
├─ 核心操作(3大组件)
│  ├─ 成员变量(Field):getDeclaredFields()/getFields() + set()/get() + 暴力反射
│  ├─ 成员方法(Method):getDeclaredMethods()/getMethods() + invoke() + 暴力反射
│  └─ 构造方法(Constructor):getDeclaredConstructors()/getConstructors() + newInstance()
├─ 实战场景:结合配置文件动态加载
└─ 注意事项:性能、安全、可读性、兼容性

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

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

相关文章

ARM相关的基础概念和寄存器

目录 1、机器码 2、汇编指令 3、汇编指令集 4、架构 5、内核&#xff08;CPU中的核心&#xff09; 6、处理器 7、复杂指令集和精简指令集 7.1复杂指令集 7.2精简指令集 7.3修改 8、内核中的寄存器阻值 8.1溜达存储类型 8.2、AMR处理器&#xff08;内核&#xff09…

PPT处理控件Aspose.Slides教程:在 C# 中将 PPTX 转换为 Markdown

将您的PowerPoint幻灯片转换为Markdown格式&#xff0c;使其兼容 AI 技术。在这个人工智能驱动的时代&#xff0c;GPT和Claude等大模型能够读取和生成基于 Markdown 的内容。此外&#xff0c;Markdown 还可用于博客文章和文档。因此&#xff0c;作为一名 .NET 开发人员&#xf…

Python 多进程(multiprocessing)

文章目录1. 多进程概述1.1. 多进程的概念1.2. 多进程注意事项2. 进程调用方式2.1. Process 类2.1.1. 构造方法2.1.2. 实例方法2.1.3. 属性2.2. 面向过程2.3. 面向对象3. 进程间通讯3.1. Queues3.2. Pipes3.3. Managers&#xff08;进行共享数据&#xff09;4. 进程同步5. 进程池…

推荐系统王树森(五)重排多样性提升

重排01&#xff1a;物品相似性的度量、提升多样性的方法_哔哩哔哩_bilibili github-PPT 前面的讨论中提到 在链路的最后进行重排&#xff0c;重排要插入广告和运营笔记&#xff0c;还要做规则打散&#xff0c;提高推荐的丰富性&#xff0c;比如说一个人特别爱看足球&#xff…

Axios多实例封装

Axios多实例封装方案 我将为您提供一个完整的Axios多实例封装方案&#xff0c;包含基础封装、多实例管理和使用示例。 设计思路 创建基础axios实例封装&#xff0c;支持请求/响应拦截器实现多实例管理器&#xff0c;支持不同API端点配置提供统一的错误处理机制支持请求取消功…

为量化从业者提供免费稳定的股票数据源:免费股票数据API获取实时数据、历史数据与技术指标(含多语言代码指南)实例代码演示与API文档说明

​ 近年来&#xff0c;股票量化分析因其科学、系统的研究方法&#xff0c;日益成为市场热点。而进入这一领域的首要步骤&#xff0c;正是获取全面、准确的股票数据——无论是实时交易行情、历史走势记录&#xff0c;还是企业财务与基本面信息&#xff0c;都是支撑量化策略的核心…

[Sync_ai_vid] UNet模型 | 音频特征提取器(Whisper)

第2章&#xff1a;LatentSync UNet模型 在第1章&#xff1a;唇形同步推理流程中&#xff0c;我们了解到唇形同步推理流程如同电影导演&#xff0c;协调各"专家模块"生成完美唇形同步视频。 本章将深入解析这个"工作室"中最核心的专家——LatentSync UNet…

工业级TF卡NAND + 北京君正 + Rk瑞芯微的应用

目录前言一&#xff1a;操作CS创世 SD NAND的常用命令1.查看SD设备2.挂载分区3.卸载分区4.分区管理5.格式化分区6.简单读写二&#xff1a;SD底层协议简要介绍三&#xff1a;对CS创世 SD NAND进行读写操作的三大方式1. 使用dd命令2. 使用块设备的标准接口3. 使用ioctl产生系统调…

深入解析Java并发编程与单例模式

目录 一、调度(四) 1.随机调度大环境 二、锁(二) 1.位置 2.无锁阻塞 3.重入锁 4.连续锁 4.1措施 三、线程方法(二) 1.wait 2.notify 3.wait-notify指位后移 3.1可能时再检查 3.1.1join(二) 3.1.1.1可能时再检查死亡 四、单例模式 1.实现 1.1private构造器 1…

java通过redis简单实现分布式锁

目录 一、简介 二、代码实现 1、maven添加依赖 2、核心逻辑代码 3、使用方式 三、总结 一、简介 我们知道在Java中可以通过关键字synchronized来防止并发引起的问题&#xff0c;而分布式中就需要考虑分布式锁了&#xff0c;今天来讲一个比较简单的实现&#xff0c;通过re…

网络编程--TCP/UDP Socket套接字

网络编程 程序员主要操作应用层和传输层来实现网络编程&#xff0c;也就是自己写一个程序&#xff0c;让这个程序可以使用网络来通信&#xff0c;这个程序属于应用层&#xff0c;实现通讯就需要获取到传输层提供的服务 这就需要使用传输层提供的api UDP: 无连接&#xff0c;不可…

claude-code订阅方案

Claude Code 订阅方案对比 编写日期&#xff1a;2025 年 08 月 20 日 &#x1f3f7;️ 专业版 Pro ($20/月&#xff0c;$200/年) 主要特性 可用模型&#xff1a;Claude Sonnet 4&#xff08;Claude Opus 4成本太高&#xff0c;谨慎选择&#xff09;适用场景&#xff1a;适合轻度…

146. LRU缓存

题目&#xff1a; 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#x…

第二十节:3D文本渲染 - 字体几何体生成与特效

第二十节&#xff1a;3D文本渲染 - 字体几何体生成与特效 TextGeometry深度解析与高级文字效果实现1. 核心概念解析 1.1 3D文字渲染技术对比技术原理优点缺点TextGeometry将字体轮廓转换为3D网格真实3D效果&#xff0c;支持材质性能开销大&#xff0c;内存占用高Canvas纹理将文…

zzz‘sJava知识点概括总结

类型转化 字符串&#xff1a;c语言&#xff1a;char Java&#xff1a;string 表达式值的类型由最高类型决定&#xff1a; 取值范围&#xff1a;byte<short<int<long<float<double&#xff08;且运算时byte和short都是转化为int类型进行计算防止数据溢出&…

SONiC 之 Testbed(2)Ansible

Ansible 是一款由 Red Hat 主导开发的 开源自动化工具&#xff0c;专注于 配置管理、应用部署、任务编排和IT自动化。它基于 无代理&#xff08;Agentless&#xff09;架构&#xff0c;通过 SSH&#xff08;默认&#xff09;或 WinRM 协议与目标设备通信&#xff0c;无需在被控…

瑞芯微RK3568与君正X2600e平台Linux系统CS创世SD NAND应用全解析与驱动架构详解

前言 今天就瑞芯微平台和北京君正平台下的linux系统中关于CS创世 SD NAND的使用做一些经验的分享&#xff0c;如有不正&#xff0c;请批评指正&#xff1b; 采用的开发板是RK3568和x2600e&#xff0c;ubuntu版本是20.04&#xff0c;交叉编译工具链是aarch64-linux-gnu-和mips…

深入解析 Flink Function

RichFunctionFunction只是个标记接口public interface Function extends java.io.Serializable {}RichFunction 的核心语义是为用户定义的函数&#xff08;UDF&#xff09;提供生命周期管理和运行时上下文访问的能力。任何一个普通的 Flink Function 接口&#xff08;例如 MapF…

JMeter —— 压力测试

目录 常用的性能指标 一、吞吐量类指标 二、响应时间类指标 三、资源利用率指标 JMeter 一、JMeter 简介 二.下载安装JMeter&#xff1a; 三.如何使用JMeter&#xff1a; 压力测试考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。压测都是为了系统…

Transformer在哪⾥做了权重共享?

1、什么是权值共享权重共享是指在模型的不同层之间复⽤相同的参数。这可以减少模型的总体参数数量&#xff0c;并使得模型在训练时更容易学习。2、在Transformer中的应用常见的做法是共享词嵌入层&#xff08;embedding layer&#xff09;和输出层&#xff08;output layer&…