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

Java笔记——反射

在 Java 的世界里,反射(Reflection)是一把双刃剑。它赋予了程序在运行时动态获取类的信息、创建对象、调用方法、访问字段的能力,让 Java 从编译时静态语言获得了动态语言的灵活性。许多框架(如 Spring、MyBatis、Hibernate)都离不开反射。但反射也带来了性能损耗和安全隐患。本文将带你全面了解 Java 反射的核心概念、使用方式、应用场景以及注意事项。

一、什么是反射?

反射是指程序在运行期可以访问、检测和修改它自身状态或行为的一种能力。通过反射,我们可以:

  • 获取任意类的名称、包信息、父类、接口等。

  • 动态创建对象实例。

  • 获取并调用任意方法(包括私有方法)。

  • 访问和修改字段的值(包括私有字段)。

  • 操作数组。

Java 反射 API 主要位于java.lang.reflect包中。

二、反射的核心类

类/接口作用
Class代表一个类或接口,反射的入口
Constructor代表构造方法
Method代表普通方法
Field代表成员变量(字段)
Array提供动态创建和访问数组的静态方法
Modifier提供解析访问修饰符的工具方法
Parameter代表方法参数(JDK 8+)

三、获取 Class 对象

要使用反射,首先需要获取目标类的Class对象。有以下三种方式:

3.1 通过类名.class

Class<?> clazz1 = String.class; Class<?> clazz2 = int.class; // 基本类型也有 Class 对象

3.2 通过对象的 getClass() 方法

String str = "hello"; Class<?> clazz = str.getClass();

3.3 通过 Class.forName() 全限定类名

Class<?> clazz = Class.forName("java.util.ArrayList");

注意:这种方式会触发类的静态初始化块(如果类还未加载)。

四、通过反射创建对象

4.1 使用无参构造器

Class<?> clazz = Class.forName("java.util.ArrayList"); Object obj = clazz.newInstance(); // 已过时,但依然可用

推荐使用Constructor来创建:

Constructor<?> constructor = clazz.getConstructor(); Object obj = constructor.newInstance();

4.2 使用有参构造器

Class<?> clazz = Class.forName("java.lang.String"); // 获取参数类型为 String 的构造器 Constructor<?> constructor = clazz.getConstructor(String.class); Object obj = constructor.newInstance("hello");

五、通过反射调用方法

5.1 调用公共方法

public class Person { public void sayHello(String name) { System.out.println("Hello, " + name); } } // 反射调用 Class<?> clazz = Person.class; Object person = clazz.newInstance(); Method method = clazz.getMethod("sayHello", String.class); method.invoke(person, "World");

5.2 调用私有方法

public class Calculator { private int add(int a, int b) { return a + b; } } // 反射调用私有方法 Calculator calc = new Calculator(); Method method = Calculator.class.getDeclaredMethod("add", int.class, int.class); method.setAccessible(true); // 压制 Java 访问检查 int result = (int) method.invoke(calc, 3, 5); System.out.println(result); // 8

六、通过反射访问字段

6.1 访问公共字段

public class User { public String name; } User user = new User(); Field field = User.class.getField("name"); field.set(user, "Alice"); System.out.println(field.get(user)); // Alice

6.2 访问私有字段

public class Student { private int age; } Student student = new Student(); Field field = Student.class.getDeclaredField("age"); field.setAccessible(true); field.set(student, 18); System.out.println(field.get(student)); // 18

七、操作数组

Array类提供了一组静态方法,可以动态创建和访问数组。

// 创建一个 int 数组,长度为 5 Object array = Array.newInstance(int.class, 5); // 设置索引 0 处的值为 10 Array.set(array, 0, 10); // 获取索引 0 处的值 int value = Array.getInt(array, 0); System.out.println(value); // 10

八、反射的应用场景

8.1 框架开发

Spring 的 IoC 容器通过反射创建 Bean 实例并注入依赖;MyBatis 通过反射将数据库查询结果映射到 Java 对象;JUnit 通过反射执行测试方法。

8.2 动态代理

JDK 动态代理基于接口生成代理类,底层使用了反射来调用目标方法。

8.3 注解处理

在运行时读取注解信息,通常结合反射实现。例如,Spring MVC 的@RequestMapping注解解析。

8.4 工具类与调试

开发通用工具(如对象深拷贝、JSON 序列化)时,反射可以避免针对每种类型编写重复代码。

九、反射的性能问题与优化

反射由于涉及动态解析、安全检查(即使setAccessible也还是有一定开销),性能远低于直接调用。在性能敏感的场景下,可以采取以下优化措施:

  1. 缓存反射对象MethodFieldConstructor对象可以缓存,避免重复获取。

  2. 关闭访问检查setAccessible(true)可以略微提升速度。

  3. 使用MethodHandle(JDK 7+):提供比反射更快的动态调用方式。

  4. 避免频繁调用反射:如果反射调用是热点代码,考虑使用代码生成或缓存代理。

性能对比示例(简单测试):

// 直接调用 for (int i = 0; i < 1_000_000; i++) { obj.method(); } // 反射调用(未缓存) for (int i = 0; i < 1_000_000; i++) { Method m = obj.getClass().getMethod("method"); m.invoke(obj); } // 反射调用(缓存 Method 对象) Method m = obj.getClass().getMethod("method"); for (int i = 0; i < 1_000_000; i++) { m.invoke(obj); }

实际测试中,缓存后的反射调用大约比直接调用慢 3-5 倍,而未缓存的反射调用则慢得多。

十、反射的安全性问题

  • 破坏封装性:通过setAccessible(true)可以访问私有成员,打破了类的封装原则,可能导致不可预知的行为。

  • 安全限制:在安全管理器(SecurityManager)启用的情况下,某些反射操作可能被禁止。

  • 框架使用场景:通常只在框架内部使用反射,业务代码应尽量避免直接使用反射,以保持代码的清晰和可维护性。

十一、总结

Java 反射机制是一种强大的工具,它使程序具备了动态性,是框架和基础库不可或缺的部分。然而,反射也是一把双刃剑:

  • 优点:提供了极高的灵活性,支持动态代理、依赖注入、注解处理等高级特性。

  • 缺点:性能损耗、破坏封装、可能引发安全问题、代码可读性降低。

在实际开发中,建议只在确实需要动态处理时才使用反射,并遵循以下原则:

  1. 缓存反射对象以减少性能开销。

  2. 尽量使用MethodHandle替代反射(如果性能是关键)。

  3. 避免在业务代码中随意使用反射,保持代码的简洁和可维护性。

理解反射的原理,不仅有助于写出更优雅的代码,也能帮助你更深入地理解 Spring、MyBatis 等主流框架的实现。希望本文能为你揭开反射的神秘面纱,让你在 Java 编程之路上走得更远。

http://www.jsqmd.com/news/530800/

相关文章:

  • 别再死记硬背了!用JavaScript手写一个三角函数计算器(附完整源码)
  • 数据库系统概论第6版第九章习题解析:从存储策略到索引优化全攻略
  • 别再死记硬背了!用Verilog实现移位寄存器的3种核心写法(附仿真对比)
  • Flowable实战:从BPMN画图到Spring Boot集成,一个请假审批系统的保姆级搭建教程
  • 如何在Python中建立高效的调试流程
  • 基于Claude Code的SDPose-Wholebody智能提示词优化方法
  • 从向量到文本:解码大模型输出背后的数学与工程实践
  • 亲测五恒系统供应商联系实践分享
  • 我电脑启动了一个WSL,如何在powershell 进入WSL
  • Qwen1.5-1.8B GPTQ模型效果深度评测:对话与代码生成能力展示
  • 如何用高效工具提升3D建模效率?STL体积计算器的技术突破与场景应用
  • 避坑指南:在Vivado/Quartus中仿真HDLbits的Module练习题时,你可能遇到的3个常见问题
  • Qwen3-ForcedAligner-0.6B企业应用:法务会议语音→带时间戳法律摘要生成
  • 终极指南:使用OpenCore Legacy Patcher让老旧Mac设备重获新生
  • PyTorch 2.8镜像效果展示:RTX 4090D跑通InternVideo2-13B多模态理解案例
  • HFSS实战解析:双频单极子天线设计中的关键参数与性能优化
  • 清音听真Qwen3-ASR-1.7B效果实测:嘈杂环境下的识别依然清晰
  • 基于PyTorch 2.8与RTX4090D的卷积神经网络(CNN)实战:从零构建图像分类模型
  • EcomGPT-中英文-7B电商模型YOLOv11技术前瞻:下一代视觉模型与文本模型的融合应用
  • 2026宁波附近发电机出租公司推荐榜:芜湖发电机租赁公司/芜湖发电机租赁电话/芜湖推荐发电机租赁公司/芜湖附近发电机出租/选择指南 - 优质品牌商家
  • 避开SpringSecurity多表登录的5个大坑:从密码加密到@Primary的完整避坑指南
  • 顺序表的增删查改
  • 5个技巧搞定多显示器DPI调节:SetDPI实战指南
  • 魔兽地图全版本兼容与修复利器:w3x2lni深度技术指南
  • 让所有游戏支持手柄:AntiMicroX新手实用指南
  • Qwen3-Embedding-4B效率提升:批量处理文本嵌入技巧分享
  • 别再死记命令了!用eNSP模拟企业双核心网络,手把手教你配置VRRP+MSTP实现负载分担
  • 从0开始学AI:层归一化,原来是这回事!
  • 2026最新windows server2016安装教程,收藏这一篇就够了
  • Sqli-labs靶场通关实战:从字符型注入到HTTP头部注入的完整指南(附Payload大全)