一文带你吃透 Java 反射机制

作者:BestAns日期:2025/12/24

一文带你吃透 Java 反射机制

在Java开发中,“反射”绝对是个让人又爱又恨的知识点。有人觉得它晦涩难懂、破坏封装,也有人靠它实现了各种灵活的功能——比如框架开发、动态配置加载。

其实反射没那么神秘,今天就给大家用最通俗的语言讲清楚:反射到底是什么、怎么用,以及反射在实际开发中的应用。

一、Java反射到底是什么?

我们先从Java的核心特性“封装”说起。平时写代码时,我们通过new关键字创建对象,调用类的方法、访问属性,都是在“编译期”就确定好要操作的类,比如User user = new User();,编译器早就知道我们要操作User类。

而反射机制,简单说就是程序在运行时,能够“看透”一个类的内部结构:知道它有哪些属性、哪些方法、哪些构造器,还能动态地创建对象、调用方法、修改属性,哪怕这些成员是私有的。

形象点说,普通方式是“先知道类,再用类”;反射是“先拿到类的‘说明书’,再根据说明书用类”。这个“说明书”,就是Java中的Class类对象。

Java中的反射,本质上是运用了:类是由JVM在执行过程中动态加载的这一原理实现的。

二、Class类是关键

在Java中,任何一个类被加载后,JVM都会为它创建一个唯一的Class对象,这个对象包含了该类的所有信息(成员变量、构造方法、成员方法等)。反射的所有操作,本质上都是通过操作这个Class对象实现的。

简单梳理反射的核心流程:

  1. 获取目标类的Class对象(拿到“说明书”);
  2. 通过Class对象获取需要的成员(属性、方法、构造器);
  3. 动态操作这些成员(创建对象、调用方法、修改属性)。

三、反射的基础用法

先铺垫基础用法,后面的案例会基于这些操作展开。我们以一个简单的User类为例:

1package com.example.demo;
2
3public class User {
4    private String name;
5    private int age;
6
7    // 无参构造
8    public User() {}
9
10    // 有参构造
11    public User(String name, int age) {
12        this.name = name;
13        this.age = age;
14    }
15
16    // 普通方法
17    public void sayHello() {
18        System.out.println("Hello, " + name + "! You are " + age + " years old.");
19    }
20}
21

第一步:获取Class对象

在Java中总共有三种方式获取Class对象:

1// 方式1:通过类名.class(编译期确定,最安全)
2Class<User> userClass1 = User.class;
3
4// 方式2:通过对象.getClass()(运行时获取,需先有对象)
5User user = new User();
6Class<? extends User> userClass2 = user.getClass();
7
8// 方式3:通过Class.forName("全类名")(动态加载,最灵活,常用)
9Class<?> userClass3 = Class.forName("com.example.demo.User"); // 全类名=包名+类名
10

其中,第三种方式最灵活,因为全类名可以来自配置文件、数据库等,实现“动态指定类”,稍后我们会详细介绍这种方式。

第二步:通过Class对象创建对象

1// 1. 通过无参构造创建(最常用)
2Class<?> userClass = Class.forName("com.example.demo.User");
3User user1 = (User) userClass.newInstance();
4
5// 2. 通过有参构造创建
6Constructor<?> constructor = userClass.getConstructor(String.class, int.class);
7User user2 = (User) constructor.newInstance("张三", 20);
8

这两种反射创建 User 实例的方式核心区别在于:前者调用无参构造器创建实例,且该方式在 Java 9 被标记为过时(@Deprecated),Java 11 彻底移除,不推荐使用;后者通过指定有参构造器创建实例,能直接传入参数完成初始化,是反射创建实例的标准推荐写法。

第三步:调用类的方法

1// 1. 获取sayHello方法(无参、public)
2Method sayHelloMethod = userClass.getMethod("sayHello");
3// 2. 调用方法(需要传入对象实例)
4sayHelloMethod.invoke(user2); // 输出:Hello, 张三
5
6// 3. 调用带参方法(比如setName)
7Method setNameMethod = userClass.getMethod("setName", String.class);
8setNameMethod.invoke(user2, "李四");
9sayHelloMethod.invoke(user2); // 输出:Hello, 李四
10

如果要操作私有成员(比如private属性name),需要先调用setAccessible(true)打破封装限制,这里就不展开说明了。

四、反射的两个经典应用场景

理解了基础用法,再看两个实际开发中常用的案例,帮助我们更加深刻的理解反射存在的意义。

案例1:读取配置文件,动态加载类并执行方法

我们希望程序不修改代码,只修改配置文件,就能加载不同的类、执行不同的方法,这在框架开发(比如Spring)中非常常见,核心就是反射。

实现步骤:

  1. 创建配置文件(比如reflect.properties),写入要加载的类名和方法名;
  2. 通过IO流读取配置文件中的类名和方法名;
  3. 用反射动态加载类、创建对象、执行方法。
步骤1:创建配置文件(reflect.properties)
1# 全类名
2className=com.example.demo.User
3# 要执行的方法名
4methodName=sayHello
5
步骤2:编写工具类读取配置文件
1package com.example.demo;
2
3import java.io.IOException;
4import java.io.InputStream;
5import java.util.Properties;
6
7public class PropertiesUtil {
8    public static Properties getProperties() {
9        Properties properties = new Properties();
10 
11        InputStream is = PropertiesUtil.class.getClassLoader().getResourceAsStream("reflect.properties");
12        try {
13            properties.load(is);
14        } catch (IOException e) {
15            e.printStackTrace();
16        } finally {
17            try {
18                if (is != null) is.close();
19            } catch (IOException e) {
20                e.printStackTrace();
21            }
22        }
23        return properties;
24    }
25}
26
步骤3:用反射动态加载并执行
1package com.example.demo;
2
3import java.lang.reflect.Method;
4import java.util.Properties;
5import java.lang.reflect.Constructor;
6
7public class ReflectDemo1 {
8    public static void main(String[] args) throws Exception {
9        // 1. 读取配置文件
10        Properties properties = PropertiesUtil.getProperties();
11        String className = properties.getProperty("className");
12        String methodName = properties.getProperty("methodName");
13
14        try {
15            // 2. 获取Class对象
16            Class<?> clazz = Class.forName(className);
17            // 3. 创建对象
18            Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
19            Object obj = constructor.newInstance("张三", 18);
20            // 4. 获取方法并执行
21            Method method = clazz.getMethod(methodName);
22            method.invoke(obj); // 执行sayHello方法
23        } catch (Exception e) {
24            e.printStackTrace();
25        }
26    }
27}
28

运行程序,会执行User类的sayHello方法。如果我们想换一个类执行,比如创建一个Order类,只需要修改配置文件中的classNamemethodName,不用修改代码就能实现,这就是反射的“动态性”价值。

案例2:实现isClassPresent方法,优先加载指定类,失败则加载默认类

我们在开发中经常会遇到“降级策略”——优先加载某个指定的类,如果该类不存在(比如依赖包未引入),就加载默认的兜底类。用反射可以轻松实现这个逻辑,定义一个isClassPresent方法来判断类是否存在并加载。

实现思路:

  1. 定义一个方法,参数为classNamedefaultClassName
  2. 尝试用Class.forName()加载指定类,若不抛异常则说明类存在,返回该类的Class对象;
  3. 若加载指定类抛ClassNotFoundException,则加载默认类并返回其Class对象。
步骤1:创建默认类和备选类
1// 默认兜底类
2public class DefaultService {
3    public void doService() {
4        System.out.println("执行默认服务逻辑");
5    }
6}
7
8// 备选指定类(可能不存在)
9public class CustomService {
10    public void doService() {
11        System.out.println("执行自定义服务逻辑");
12    }
13}
14
步骤2:实现isClassPresent方法
1package com.example.demo;
2
3public class ClassLoaderUtil {
4    /**
5     * 优先加载指定类,失败则加载默认类
6     * @param className  要优先加载的类全类名
7     * @param defaultClassName  兜底的默认类全类名
8     * @return  加载成功的Class对象
9     * @throws ClassNotFoundException  若默认类也不存在则抛异常
10     */
11    public static Class<?> isClassPresent(String className, String defaultClassName) throws ClassNotFoundException {
12        try {
13            // 优先加载指定类
14            return Class.forName(className);
15        } catch (ClassNotFoundException e) {
16            System.out.println("指定类[" + className + "]不存在,加载默认类[" + defaultClassName + "]");
17            // 加载失败,加载默认类
18            return Class.forName(defaultClassName);
19        }
20    }
21}
22
23
步骤3:测试验证
1package com.example.demo;
2
3import java.lang.reflect.Method;
4import java.lang.reflect.Constructor;
5
6public class ReflectDemo2 {
7    public static void main(String[] args) throws Exception {
8        // 场景1:指定类存在(CustomService已创建)
9        Class<?> clazz1 = ClassLoaderUtil.isClassPresent(
10            "com.example.demo.CustomService", 
11            "com.example.demo.DefaultService"
12        );
13        Constructor<?> constructor1 = clazz1.getConstructor();
14        Object obj1 = constructor1.newInstance();
15        Method method1 = clazz1.getMethod("doService");
16        method1.invoke(obj1); // 输出:执行自定义服务逻辑
17
18        // 场景2:指定类不存在(故意写一个错误的类名)
19        Class<?> clazz2 = ClassLoaderUtil.isClassPresent(
20            "com.example.demo.NonExistentService", // 不存在的类
21            "com.example.demo.DefaultService"
22        );
23        Constructor<?> constructor2 = clazz2.getConstructor();
24        Object obj2 = constructor2.newInstance();
25        Method method2 = clazz2.getMethod("doService");
26        method2.invoke(obj2); // 输出:指定类[com.example.NonExistentService]不存在,加载默认类[com.example.DefaultService] + 执行默认服务逻辑
27    }
28}
29

这个逻辑在实际开发中很实用,比如:

  • 框架的插件化开发:优先加载用户自定义的插件类,没有则用默认实现;
  • 依赖降级:当某个第三方依赖包未引入时,自动切换到本地默认实现,避免程序崩溃。

五、写在最后

Java的反射,打破了Java程序在编译期的束缚,能在运行时动态加载类、执行方法,既大幅提升了程序的灵活性与扩展性,也减少了类间的硬编码依赖。更关键的是,反射是诸多Java核心技术的基石,比如:Spring IOC、MyBatis Mapper映射、JUnit单元测试框架等,底层都离不开它的支撑。

但我们也不能忽视它的“另一面”,反射对私有成员的访问会破坏面向对象的封装原则,解析Class对象、验证权限等操作带来的性能开销,这就要求我们在使用Java反射时保持谨慎。

掌握反射,本质上是打通了“使用框架”到“理解框架底层原理”的关键一环。

如果觉得有用,欢迎点赞、在看、转发三连~ 有疑问也可以在评论区留言交流~

更多精彩文章,欢迎关注我的公众号:前端架构师笔记


一文带你吃透 Java 反射机制》 是转载文章,点击查看原文


相关推荐


为什么说 AI 赋能前端开发,已经不是选择题,而是必然趋势?
西陵2025/12/16

首发于公众号 code进化论,欢迎关注。 前言 这几年 AI 在前端开发里的能力几乎是肉眼可见地进化”。从最早只能帮我们做做代码补全、提示几个参数,例如早期的 comate。到后来能够独立生成一个完整的 React/Vue 组件,连逻辑、样式和交互都能自动写好,例如 cursor 和 claude。再到现在,AI 已经能根据一句自然语言去搭建整个前端项目,自动创建页面、路由、接口层,甚至跑通基础业务流程,例如 v0 和 bolt.new。AI 的角色正在从“聪明的编辑器”变成“能独立干活的虚拟工


计算机十万个为什么--数据库索引
无限大62025/12/8

计算机十万个为什么--数据库索引 大家好,欢迎来到最新一期的无限大博客。 突然发现自己对数据库相关的内容掌握不够扎实,于是就去学习了一下,顺便也将自己的理解写成了一篇博客。 希望这篇文章能对大家有所帮助 数据库索引:给数据仓库装个"智能导航系统" 🧭 想象一下,你走进一个占地 1000 平方米的超级图书馆 📚,里面塞满了几十万本书,却连个分类牌都没有。老板忽然喊你找一本《数据库从入门到放弃》,你是不是当场想表演一个原地消失术?😱 这就是没有索引的数据库的日常!每次查询都像蒙眼找书,全表


失业7个月,我把公司开起来了:一个程序媛的“野蛮生长”
后端小肥肠2025/11/28

大家好,我是小肥肠。 4月被裁,11月注册公司。 这7个月,我一个人赚回了以前一年的工资,也攒够了人生第一台CC的首付。今天不讲技术,聊聊这半年一个程序媛的野蛮生长。 1. 半年了我开起了公司 从4月到现在已经创业半年多了(7个月),这7个月以来,我从一个一无所有的失业人到现在攒够了一台cc的首付(赚的比以前上班一年还多),我的共学社群实现了从0到现在的300多人。 其中有很多和我一样的程序员,他们都是被我的文章吸引来共学群一起成长,也有很多小白进来一步一步成长为可以自行搭建自己的智能体。 在这


2025年终总结,智启
袁庭新2026/1/3

大家好,我是袁庭新。2025年就这么溜走了,对我而言,是极为不寻常的一年,总是想着用文字把它记录下来。 文章输出 写是为了更好的思考,坚持写作,力争更好的思考。 2025年累计发表54篇原创文章,平均1周更1篇,大多数是技术相关。2025年我有个转变——每个月写一篇月总结,对这个月主要做了什么事做一个系统的梳理,尽量以可量化的形式呈现,比如,这个月写了多少篇文章,拍了几条短视频,录了几节课,办了几场讲座等诸如此类。 为什么采用这种方式呢?前些年我也不是没写过年终总结,年底一回顾,感觉又稀里糊涂过


WPF样式进阶实战:外置样式+MVVM主题切换+样式优先级全解析
bugcome_com2026/1/11

在WPF开发中,样式(Style)是实现界面美化、统一风格、提高代码复用性的核心利器。但很多开发者在实际项目中,容易陷入「内联样式冗余」「主题切换困难」「样式优先级混乱」的困境,写出难以维护的XAML代码。 今天我们就通过一个完整的模块化实战项目(附全部可运行代码),从「外置样式封装」到「MVVM模式主题切换」,再到「样式优先级核心知识点」,全方位解锁WPF样式的高级用法,最终实现一个支持「浅/深色全局主题切换」「按钮专属样式切换」「传统后台代码样式切换」的完整案例。 一、项目架构梳理:模块化让


万字长文!搞懂机器学习中的概率图模型
aicoting2026/1/19

推荐直接网站在线阅读:aicoting.cn 概率图模型(Probabilistic Graphical Models, PGM)是一类结合概率论与图论的强大工具,用于描述多个随机变量之间的依赖关系。它通过图结构将复杂的联合概率分布分解为局部条件概率分布,使得对高维数据建模和推断变得可行且高效。 根据图的类型,PGM 可分为有向图模型(如贝叶斯网络)和无向图模型(如马尔可夫随机场)。贝叶斯网络利用有向无环图表示变量之间的因果关系,适合建模因果推断和序列数据;马尔可夫随机场则通过无向图捕捉变量之


耗时 20 天,AI 漫剧 APP 和 Web 全部开源, 已斩获 764 星!
苍何2026/1/28

这是苍何的第 474 篇原创! 大家好,我是消失了一段时间的苍何。 1 月 5 号,我写了篇文章,并开源了 AI 漫剧 APP,获得了很多朋友的喜欢。 然后在 GitHub 上一共获得了 764 星和 181 fork,让我有些吃惊。 说实话,这个项目远超我们的预期,甚至连歪果哥都来给我们提 issue,希望支持双语。 甚至还有老板来咨询问我卖不卖这个 APP,我说,大哥,咱都开源了,自己去整吧,不用付费,哈哈哈。 这或许就是开源的魅力吧。 但我发现,APP 还是不大方便,评论区也不少求


提示词工程入门-03
一诺滚雪球2026/2/6

前言 "写个代码" "帮我写个快速排序函数,用 Python 实现,要求时间复杂度 O(n log n),添加详细注释" 同样是让 AI 写代码,为什么第一个指令得到的是模糊的回复,而第二个能得到精确满足需求的代码? 这就是提示词工程(Prompt Engineering)的魔力。 好的 Prompt = 好的输出。今天我们来学习如何写出让 AI "秒懂"的提示词。 1. 什么是提示词工程 提示词(Prompt):你给大模型的输入指令 提示词工程(Prompt Engineering):设计和


EasyExcel的使用
脸大是真的好~2026/2/15

需求1:能够导出1个Excel文件,能够导入一个Excel文件; 需求2:导出的文件,能实现第1行,第123列的合并单元格:也就是会写注册处理器;知道sheet和cell是什么; 需求3:能实现合并的单元格设置单元格宽高,背景颜色,内容居中,字体大小; 需求4:能控制从任意行开始写入,并让要输出的字段居中; 需求5:导出能实现从任意行开始读入; 导出Excel文件 <!-- EasyExcel 核心依赖 --> <dependency> <groupId>com.alibaba</gro


一个简单Demo彻底理解前后端怎么连的丨Figma + Supabase + Vercel
阿星AI工作室2026/2/23

哈喽,大家好! 我是阿星👋 很多小白编程学了三个月,全是AI做主UI,难以融入自己的设计理念。 甚至不了解前后端到底怎么连通的。 一旦代码出错了,可能和AI对话还要重新理解一遍概念。所以今天,我们通过一个简单的case,把一个完整前后端的核心链路全跑一遍👇🏻 让你能自己把控UI、把控数据库、把控前端、后端。 🗺️ 先看一眼全局流程 整件事分五步,每一步做完了才能进下一步: ① Figma 画页面 →  ② 定接口契约 →  ③ Supabase 建数据库→  ④ AI 帮你写连接代码

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客