一文带你吃透 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 的角色正在从“聪明的编辑器”变成“能独立干活的虚拟工


CSDN AI社区镜像创作者征集计划正式启动,参与即可获得奖励哦~
CSDN官方博客2025/12/8

CSDN AI 社区是聚焦 AI 技术产业落地的开发者服务平台(官方入口),核心为创作者搭建技术价值转化桥梁,AI社区涵盖四大核心模块:算力市场、模型广场、社区镜像、Agent市场。 社区镜像是CSDN AI社区为开发者提供的核心技术分享与变现载体,可理解为封装了特定AI开发环境、模型或项目的标准化资源包,核心特点与价值如下: 1. 内容核心:包含完整依赖库、框架配置(如TensorFlow/PyTorch)、模型权重、推理代码等,解决环境适配、部署繁琐问题; 2. 使用价值:用户可直


Rokid AI眼镜:连接现实与数字的桥梁,探索下一代智能应用开发
倔强的石头_2025/11/28

@[toc] 前言:当AI遇上AR,未来触手可及 增强现实(AR)技术长久以来都被视为下一代计算平台,它承诺将数字信息无缝叠加到物理世界之上,从而彻底改变我们与信息交互的方式。然而,要将这一愿景变为现实,离不开一个强大、易用且充满活力的开发生态。Rokid AI眼镜及其配套的SDK,正是这样一个旨在赋能开发者的平台,它为我们打开了通往“空间互联网”时代的大门。 本文将聚焦于AI Glasses实践应用,以一个具体的工业场景——AI工业装配助手为例,深入探讨如何利用Rokid平台提供的能力,从概念


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 可分为有向图模型(如贝叶斯网络)和无向图模型(如马尔可夫随机场)。贝叶斯网络利用有向无环图表示变量之间的因果关系,适合建模因果推断和序列数据;马尔可夫随机场则通过无向图捕捉变量之

首页编辑器站点地图

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

Copyright © 2026 XYZ博客