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

【java基础难点:“反射“】别再觉得它难了!一文让你彻底理解!

目录

1.问题初现

2.反射登场

反射的原理

反射在这个例子中干了什么?

3.反射应该怎么用

先准备一个测试类:Person.java

用反射操作它

4.反射到底用在哪些地方?

框架的依赖注入(Spring)

单元测试框架(JUnit)

动态代理(AOP)

通用 JSON 转换工具

5.反射的优缺点

优点

缺点

6.什么时候用反射

7.总结

前言:

反射这个词语想必大家都不陌生吧,在初学javase的时候这个点是很多人都无法理解的,这到底是什么玩意儿啊!!!,别慌今天我带大家彻底理解反射,不光从概念理解,更从实际代码出发,现在跟着我一起学习反射。

1.问题初现

你现在写了这样一段代码

// 编译时,你定义了两个动物类 class Dog { public void speak() { System.out.println("汪汪"); } } class Cat { public void speak() { System.out.println("喵喵"); } }

然后你想当用户输入dog时,程序输出:”汪汪“,输入猫时,程序输出:”喵喵“,你是不是会想到直接用if----else来判断,那如果现在有100只动物呢,你会一个个去判断吗,很麻烦对吧,用循环吗,怎么循环?是不是不知道怎么办了,那么反射来拯救你。

2.反射登场

反射是 Java 提供的一种运行时自省机制,它允许程序在运行期间动态地获取类的完整信息(比如类名、方法、字段、构造器),并且可以操作这些成员——包括创建对象、调用方法、修改字段值,甚至访问私有成员。

它的核心是Class对象。每个类在加载到 JVM 后,都会对应一个Class对象,里面存着这个类的所有结构信息。反射就是通过这个Class对象来“窥探”和操作类的内部。

反射的原理

以上面的问题为例子,现在写一个反射代码

String animalType = 用户输入; // 运行时才能确定,比如 "Dog" // 反射动态加载类、创建对象、调用方法 Class<?> clazz = Class.forName(animalType); // 根据字符串加载类 Object animal = clazz.newInstance(); // 创建实例 ==new animalType() Method method = clazz.getMethod("speak"); // 获取 speak 方法 method.invoke(animal); // 调用方法==animal.speak()

要彻底理解反射,必须知道 Java 虚拟机(JVM)的类加载机制:

  • 当你编写一个.java文件,编译后得到.class字节码文件。

  • 当程序运行到需要用到某个类时,JVM 的类加载器会将这个类的.class文件读入内存,并为之创建一个Class对象

  • 这个Class对象就像该类的“元数据”代表,它包含了这个类的所有信息:类名、父类、实现的接口、构造器列表、方法列表、字段列表等。

这个class对象就是反射的入口

反射在这个例子中干了什么?

  1. 动态加载类:用Class.forName("Dog")Dog这个类加载到内存,得到它的Class对象。如果没有反射,你只能用new Dog(),但那样类名就写死了。

  2. 动态创建对象clazz.newInstance()相当于new Dog(),但这里事先不知道具体是哪个类。

  3. 动态查找方法getMethod("speak")找到类里的speak方法。

  4. 动态调用方法invoke(animal)实际执行animal.speak()

整个过程都是程序运行时根据用户的输入现场决定的,而不是编译时就写死。这就是反射的价值:让程序在运行时拥有“临时改变主意”的能力,变得非常灵活.

3.反射应该怎么用

反射的套路非常固定,记住这四步:

  1. 获取Class对象

  2. 通过Class对象获取构造器、方法或字段

  3. 设置可访问(如果是私有成员,需要setAccessible(true)

  4. 调用方法或修改字段

先准备一个测试类:Person.java

public class Person { private String name; public int age; public Person() {} private Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } private void setName(String name) { this.name = name; } public String getName() { return name; } public void sayHello() { System.out.println("Hello, I'm " + name); } }

用反射操作它

import java.lang.reflect.*; public class ReflectionDemo { public static void main(String[] args) throws Exception { // 1. 获取 Class 对象(三种常用方式) Class<?> clazz = Class.forName("Person"); // 方式1:类名字符串 // Class<?> clazz = Person.class; // 方式2:类名.class // Person p = new Person(); Class<?> clazz = p.getClass(); // 方式3:对象.getClass() // 2. 创建对象(通过公有构造器) Constructor<?> constructor = clazz.getConstructor(String.class, int.class); Object obj = constructor.newInstance("张三", 25); // 相当于 new Person("张三", 25) System.out.println(obj); // 输出 Person{name='张三', age=25} // 3. 操作私有字段 name Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); // 暴力反射:允许访问私有 nameField.set(obj, "李四"); // 修改私有字段的值 System.out.println("修改后 name = " + nameField.get(obj)); // 读取私有字段 // 4. 调用私有方法 setName Method setNameMethod = clazz.getDeclaredMethod("setName", String.class); setNameMethod.setAccessible(true); setNameMethod.invoke(obj, "王五"); // 相当于 obj.setName("王五") System.out.println("调用私有方法后 name = " + nameField.get(obj)); // 5. 调用公有方法 sayHello Method sayHelloMethod = clazz.getMethod("sayHello"); sayHelloMethod.invoke(obj); // 输出 Hello, I'm 王五 } }

看到没?我们全程没有直接写new Person(),也没有直接调用obj.setName(),但通过反射,我们做到了完全相同的事情,而且类名、方法名、字段名都可以是字符串变量。

4.反射到底用在哪些地方?

框架的依赖注入(Spring)

Spring 的配置文件里写着<bean class="com.example.UserService"/>,它怎么知道这个类在哪里?就是通过反射加载的:

Class<?> clazz = Class.forName("com.example.UserService"); Object userService = clazz.newInstance();

单元测试框架(JUnit)

JUnit 会扫描你写的测试类,找到所有加了@Test注解的方法,然后用反射调用它们:

for (Method method : testClass.getMethods()) { if (method.isAnnotationPresent(Test.class)) { method.invoke(testInstance); } }

动态代理(AOP)

Spring AOP、MyBatis 的 Mapper 动态代理,都是通过反射 + 动态代理生成的。

通用 JSON 转换工具

String json = "{"; for (Field field : obj.getClass().getDeclaredFields()) { field.setAccessible(true); String fieldName = field.getName(); Object value = field.get(obj); json += "\"" + fieldName + "\":\"" + value + "\","; } json = json.substring(0, json.length() - 1); // 去掉最后一个逗号 json += "}"; {"name":"张三","age":25}

obj.getClass().getDeclaredFields()

  • obj是某个对象,比如一个Person实例。

  • obj.getClass()获取这个对象的Class对象(就是前面说的“类的身份证”)。

  • getDeclaredFields()返回这个类中所有声明的字段,不管它是public还是private。返回的是一个Field数组,数组里每个元素代表一个字段。

for (Field field : 字段数组)

这就是一个增强 for 循环,依次处理每一个字段。

field.setAccessible(true);

  • 默认情况下,反射不能访问私有字段(比如private String name)。

  • setAccessible(true)就是“暴力打开”访问权限,告诉 Java:我知道这个字段可能是私有的,但我就是要访问它,你放行。这样后面才能读取或修改这个字段的值。

String fieldName = field.getName();

获取字段的名字,比如字段名叫"name""age"

Object value = field.get(obj);

获取这个字段在当前对象obj中的实际值

  • 如果字段是nameobjname"张三",那么value就是字符串"张三"

  • 如果字段是age,值是25,那么value就是整数25(注意此时value类型是Object,但实际值是Integer)。

5.反射的优缺点

优点

  • 灵活性高:可以动态加载类、调用方法,代码高度抽象。

  • 扩展性强:支持插件化架构,无需修改核心代码就能扩展功能。

缺点

  • 性能稍慢:反射比直接调用慢几十倍(因为有类型检查、权限检查等),在循环中慎用。

  • 可能破坏封装setAccessible(true)可以调用私有方法,如果滥用会破坏类的封装性,带来安全风险。

  • 代码复杂:反射代码往往比直接调用冗长,且需要处理一大堆受检异常(ClassNotFoundExceptionNoSuchMethodExceptionInvocationTargetException等)。

6.什么时候用反射

你明确知道要操作哪个类,比如new Person()不用反射,直接写
类名、方法名来自配置文件、用户输入用反射,动态处理
你在写一个通用框架或工具(如 JSON 转换、ORM)用反射,处理未知类型
你想在单元测试中调用私有方法用反射(配合setAccessible
核心业务逻辑,对性能要求极高尽量不用反射,或使用缓存、MethodHandle优化

反射是一把双刃剑,它让 Java 从静态语言变得“动态”起来,但也要在正确的地方使用它。希望这篇文章能帮你揭开反射的神秘面纱,下次看到反射代码时,能轻松看懂它的意图。

7.总结

反射并不神秘,它只是一种手段,能让程序员操作未知的事物,让程序变得非常灵活,如果这篇文章对你有帮助请给个赞或者关注,创作不易感谢你的阅读。

http://www.cnnetsun.cn/news/1290897.html

相关文章:

  • frp 内网穿透工具详细使用指南
  • 三大Java工具库:Hutool vs Guava vs Commons
  • 【量化工具推荐】期货量化交易品种与合约信息平台对比
  • StepFun团队首创图像生成模型的“虚拟GUI环境“评测基准
  • 【量化工具推荐】期货量化交易等待与驱动机制平台对比:8款平台深度分析
  • 避开渲染坑!有哪些靠谱的云渲染平台值得选?
  • 2026最新Postman安装教程[简单易懂]附安装包
  • OpenClaw部署失败怎么办?云服务器常见问题汇总
  • ABoVE:加拿大西北地区土壤有机质的燃烧严重程度,2014-2015 年
  • 从「设计优先」到「实践优先」:构建自学习 AI Agent 的技能生态系统
  • ros2 jazzy下的gazebo仿真与moveit
  • JAVA三大版本与JDK、JRE、JVM
  • 题目2267:蓝桥杯2016年第七届真题-取球博弈
  • 导远科技 × 银河通用 —— 车规级 IMU 赋能具身智能机器人场景
  • Maven依赖下载网址
  • 面试常问:TCP相关(中级篇)问题原因即解决方案
  • Flutter 三方库 lyrebird 的鸿蒙适配指南 - 录制与回放网络流量、在鸿蒙端实现极致的离线仿真自动化测试
  • Flutter 三方库 laravel_exception 鸿蒙适配指南 - 实现工业级跨端错误对账与异常监控防线
  • Flutter 三方库 serverpod_auth_client 鸿蒙适配指南 - 实现工业级全栈身份认证与多维安全治理实战
  • OJ53 54 55
  • Flutter 三方库 sample_downloader 鸿蒙适配指南 - 实现海量样本数据自动化并行采集、在 OpenHarmony 上打造高效下载调度防御线实战
  • 【数据结构算法】二分查找
  • pikachu靶场——SQL-Inject—2(Kali系统)占位符
  • 深入了解JVM
  • 通过git上传代码到gitlab(包含第一次上传)小结
  • java毕业设计——基于JSP+sqlserver的人事管理系统设计与实现(毕业论文+程序源码)——人事管理系统
  • 【云计算】AWS核心服务解析:Dynamo到RDS全览
  • Visual Studio 2013 - 调试模式下根据内存地址查看内存
  • wow-byte字节操作说明
  • DevSecOps依赖安全:10个必备工具保护你的软件供应链