news 2026/6/10 15:47:14

Java反射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java反射

一、先搞懂:反射到底是个什么东西?

反射就是程序在运行的时候,能够动态获取类的所有信息(比如成员变量、方法、构造器),并且还能直接操作这些信息的一种机制

举个通俗的例子:平时我们写Java代码,都是先知道要用到哪个类,然后new一个对象,再调用它的方法——就像你知道要找小明借东西,直接走到他面前说“把东西借我用用”。而反射不一样,它是你不知道具体找哪个人,但能通过某种“侦查手段”找到这个人,还能强行用他的东西——这就是“动态”的意思,运行时才确定要操作的类和对象。

二、为什么要学反射?

刚开始我也疑惑:好好的直接调用方法不香吗?为啥非要搞反射这么复杂的东西?总结下来有3个核心作用:

  1. 实现动态创建对象和调用方法:这是最核心的作用。比如我们写一个通用的工具类,需要适配不同的类,这时候就不能把类名写死,而是通过反射动态获取类信息、创建对象。

  2. 突破类的封装性,操作私有成员:平时我们写的private成员变量、private方法,外部是没法直接访问的,但反射可以。比如有时候我们需要修改一个类的私有变量,又不想改这个类的源码(比如用别人写的jar包),这时候反射就派上用场了。

  3. 实现通用编程,提高代码复用性:比如写一个通用的对象拷贝工具、通用的数据库操作工具,不需要针对每个类单独写代码,而是通过反射获取类的成员变量,动态赋值和读取,大大减少重复代码。

这里要提醒一句:反射虽然强大,但也不能随便用,因为它打破了封装性,会让代码的安全性降低,而且运行效率比直接调用要低一点,平时开发中如果能直接调用,就别用反射~

三、新手入门:反射的核心API实操(附代码示例)

理论讲再多不如写一遍代码,反射的核心操作其实就围绕3个步骤:

获取Class对象 → 通过Class对象获取类的内部信息(成员变量、方法、构造器) → 操作这些内部信息(创建对象、调用方法、修改变量)

下面用一个简单的Student类来演示

第一步:先定义一个测试用的Student类

public class Student { // 成员变量(包含public和private) public String name; private int age; // 构造器(无参和有参) public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } // 成员方法(包含public和private) public void study() { System.out.println(name + "正在学习Java反射~"); } private void eat(String food) { System.out.println(name + "正在吃" + food); } @Override public String toString() { return "Student{name='" + name + "', age=" + age + "}"; } }

第二步:核心操作1:获取Class对象

要使用反射,第一步必须获取目标类的Class对象,这是反射的入口:

public class ReflectionDemo { public static void main(String[] args) throws ClassNotFoundException { // 方式1:通过类名.class获取(最常用,编译时就确定) Class<?> clazz1 = Student.class; // 方式2:通过对象.getClass()获取(需要先创建对象,适合已经有实例的情况) Student student = new Student(); Class<?> clazz2 = student.getClass(); // 方式3:通过Class.forName("全类名")获取(动态加载,最灵活,运行时确定) // 注意:全类名是包名+类名,比如我的Student类在com.test包下 Class<?> clazz3 = Class.forName("com.test.Student"); // 验证一下:这三个Class对象是同一个(一个类只有一个Class对象) System.out.println(clazz1 == clazz2); // true System.out.println(clazz1 == clazz3); // true } }

这里要注意:Class.forName()方法需要处理ClassNotFoundException异常,要么throws要么try-catch

第三步:核心操作2:通过Class对象操作类的内部信息

获取到Class对象后,就可以通过它的API获取成员变量、方法、构造器,然后进行操作了。下面分场景演示几个常用的操作:

场景1:通过反射创建对象(两种方式)
public class ReflectionDemo { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<?> clazz = Class.forName("com.test.Student"); // 方式1:通过无参构造器创建对象(需要类有public的无参构造器) Object student1 = clazz.newInstance(); System.out.println(student1); // 输出:Student{name='null', age=0} // 方式2:通过有参构造器创建对象 // 1. 先获取有参构造器:参数是构造器的参数类型.class Constructor<?> constructor = clazz.getConstructor(String.class, int.class); // 2. 调用构造器的newInstance()方法创建对象,传入实际参数 Object student2 = constructor.newInstance("小明", 20); System.out.println(student2); // 输出:Student{name='小明', age=20} } }
场景2:通过反射获取和修改成员变量(包括private)
public class ReflectionDemo { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<?> clazz = Class.forName("com.test.Student"); Object student = clazz.newInstance(); // 1. 获取public成员变量name并修改 Field nameField = clazz.getField("name"); nameField.set(student, "小红"); // 给student对象的name属性赋值 System.out.println(nameField.get(student)); // 获取name属性的值,输出:小红 // 2. 获取private成员变量age并修改(需要先设置setAccessible(true)打破封装) Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); // 关键:允许访问private成员 ageField.set(student, 19); // 给private的age属性赋值 System.out.println(ageField.get(student)); // 获取age属性的值,输出:19 } }

这里重点说一下:getField()只能获取public的成员变量,要获取private的必须用getDeclaredField(),并且要调用setAccessible(true)来关闭Java的访问检查,这样才能操作private成员。

场景3:通过反射调用成员方法(包括private)
public class ReflectionDemo { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<?> clazz = Class.forName("com.test.Student"); Object student = clazz.newInstance(); // 1. 先给name属性赋值,方便方法输出 Field nameField = clazz.getField("name"); nameField.set(student, "小李"); // 2. 调用public方法study() Method studyMethod = clazz.getMethod("study"); studyMethod.invoke(student); // 调用方法,输出:小李正在学习Java反射~ // 3. 调用private方法eat(String food) Method eatMethod = clazz.getDeclaredMethod("eat", String.class); // 第二个参数是方法的参数类型.class eatMethod.setAccessible(true); // 允许访问private方法 eatMethod.invoke(student, "汉堡"); // 调用方法,传入参数,输出:小李正在吃汉堡 } }

这里要注意:

getMethod()获取public方法,getDeclaredMethod()获取所有方法(包括private);

调用方法时用invoke(),第一个参数是要调用方法的对象,后面的参数是方法的实际参数。

四、需要注意的点

  1. 忘记处理异常:反射的大部分API都会抛出checked异常(比如ClassNotFoundException、NoSuchMethodException),新手容易忘写try-catch或者throws,导致编译报错。

  2. 用getField()获取private变量:getField()只能获取public的,获取private的必须用getDeclaredField(),还要加setAccessible(true)。

  3. 获取有参构造器时参数类型写错:比如构造器是Student(String name, int age),获取时写成getConstructor(String.class, String.class),就会报NoSuchMethodException。

  4. 调用invoke()时忘记传对象:非静态方法必须传入要调用的对象,静态方法可以传null。

  5. 认为反射能修改final变量:虽然反射能获取final变量,但修改它的结果是不确定的,不同JVM可能有不同表现,尽量不要这么做。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 21:12:29

华为云国际站代理商的ESW主要有什么作用呢?

华为云国际站代理商的 ESW&#xff08;企业交换机&#xff09;&#xff0c;核心是基于二层连接网关&#xff08;L2CG&#xff09;VXLAN 构建云 - 下大二层隧道&#xff0c;解决云上 / 云下子网重叠互通、业务无改 IP 无缝迁移、混合云组网与容灾等难题&#xff0c;是跨境 / 出海…

作者头像 李华
网站建设 2026/6/10 10:55:26

【完整源码+数据集+部署教程】樱桃成熟度检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]

一、背景意义 随着全球人口的增长和人们生活水平的提高&#xff0c;水果消费量逐年上升&#xff0c;樱桃作为一种营养丰富且口感独特的水果&#xff0c;受到越来越多消费者的青睐。然而&#xff0c;樱桃的成熟度直接影响其市场价值和消费者的购买决策&#xff0c;因此&#xff…

作者头像 李华
网站建设 2026/6/10 12:26:04

Java String API完全指南:从入门到实战

在Java编程的世界里&#xff0c;String类可能是我们最早接触、使用最频繁的类之一。它看似简单&#xff0c;却隐藏着许多精妙的设计和实用的功能。让我们深入探索掌握String API。一、String基础&#xff1a;不可变的艺术1.1 String的不可变性java// String对象一旦创建就不可修…

作者头像 李华
网站建设 2026/6/10 11:10:59

跨应用流程自动化难落地?Open-AutoGLM实施避坑指南(仅限前1000人阅读)

第一章&#xff1a;Open-AutoGLM跨应用操作Open-AutoGLM 是一款基于大语言模型的自动化工具框架&#xff0c;支持在多个应用程序之间执行智能任务流转。通过标准化接口封装与上下文感知引擎&#xff0c;开发者可实现跨平台数据抓取、指令传递与状态同步。配置多应用连接 在使用…

作者头像 李华
网站建设 2026/6/8 22:10:45

Windows操作系统深度解析:从内核架构到全球应用的全面透视

1 概述&#xff1a;数字世界的基石Windows操作系统是由微软&#xff08;Microsoft&#xff09;开发的图形化操作系统&#xff0c;自1985年诞生以来&#xff0c;已成为全球个人计算机和应用生态中最具影响力的系统之一。截至2025年&#xff0c;Windows在桌面操作系统市场的份额仍…

作者头像 李华
网站建设 2026/6/10 11:10:40

如何为移动端优化anything-llm的响应格式与大小?

如何为移动端优化 Anything-LLM 的响应格式与大小&#xff1f; 在移动设备日益成为用户获取信息主要入口的今天&#xff0c;将大语言模型&#xff08;LLM&#xff09;能力无缝集成到手机端&#xff0c;已成为智能知识系统落地的关键挑战。尽管像 Anything-LLM 这样的本地化AI平…

作者头像 李华