Java反射机制之运行时"透视"类的秘密

 更新时间:2026年06月16日 08:58:06   作者:MalouP  
这段文章详细介绍了Java反射机制的核心概念、API及应用场景,强调了反射的双刃剑特性,既强大又可能带来性能、安全性和代码可读性问题等问题,并同时通过实例展示了反射在框架开发、动态代理及序列化等反序列化中的应用,提醒开发者合理使用反射以保持代码的灵活性与可读性性

一、一个让我困惑的问题

  学Java面向对象时,老师常说:"先定义类,再创建对象,然后调用方法。" 这很合理。
  但后来我接触到一些框架(比如Spring、MyBatis),发现它们有个"邪门"的能力:
  在运行时,它们能创建一个类的对象、调用它的方法,甚至修改私有字段的值——而这一切,事先根本不知道这个类是什么!

这是怎么做到的?
答案就是 Java反射机制(Reflection)。

二、反射是什么?

一句话概括:在运行时动态地获取类的信息,并操作类或对象的能力。正常写代码时,类的结构在编译期就确定了:

Student s = new Student();  // 编译时就知道有 Student 类
s.study();                 // 编译时就知道有 study() 方法

但反射不一样,它让程序在运行时才决定去操作哪个类:

// 运行时才知道要操作 "Student" 这个类
Class<?> clazz = Class.forName("com.example.Student");
// 运行时才知道要调用 "study" 这个方法
Method method = clazz.getMethod("study");
method.invoke(clazz.newInstance());

三、反射的核心API

Java的反射API主要集中在 java.lang.reflect 包下,核心就四个类:

作用
Class代表一个类的"元信息",是反射的入口
Field代表类的成员变量
Method代表类的方法
Constructor代表类的构造方法

四、实战:用反射"解剖"一个类

假设我们有一个普通的类:

public class Person {
    private String name;
    public int age;
    public Person() {}
    private Person(String name) {
        this.name = name;
    }
    public void sayHello() {
        System.out.println("Hello, I'm " + name);
    }
    private void secret() {
        System.out.println("This is private!");
    }
}
  • 获取Class对象的三种方式
// 方式1:类名.class(最常用,编译期检查)
Class<Person> clazz1 = Person.class;
// 方式2:对象.getClass()(已有对象时用)
Person p = new Person();
Class<? extends Person> clazz2 = p.getClass();
// 方式3:Class.forName()(动态加载,最灵活)
Class<?> clazz3 = Class.forName("com.example.Person");
  • 获取构造方法并创建对象
// 获取所有 public 构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 获取指定构造方法(包括 private)
Constructor<Person> privateCon = clazz.getDeclaredConstructor(String.class);
privateCon.setAccessible(true);  // 暴力破解访问权限!
Person p = privateCon.newInstance("Alice");
  • 获取并调用方法
// 获取 public 方法(包括继承的)
Method sayHello = clazz.getMethod("sayHello");
sayHello.invoke(p);  // 输出: Hello, I'm Alice
// 获取 private 方法
Method secret = clazz.getDeclaredMethod("secret");
secret.setAccessible(true);
secret.invoke(p);    // 输出: This is private!
  • 获取并修改字段
// 获取 public 字段
Field ageField = clazz.getField("age");
ageField.set(p, 20);
System.out.println(p.age);  // 20
// 获取 private 字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "Bob");

五、反射到底有什么用?

光会API不够,要知道什么时候用。反射的典型应用场景:

  • 框架开发(Spring、MyBatis、JUnit)
    Spring 的依赖注入(DI)就是靠反射实现的:
// Spring 读取配置文件后,大概是这样创建对象的:
Class<?> beanClass = Class.forName("com.example.UserService");
Object bean = beanClass.newInstance();
// 然后反射调用 set 方法注入依赖
Method setDao = beanClass.getMethod("setUserDao", UserDao.class);
setDao.invoke(bean, new UserDao());
  • 动态代理(AOP的基础)
    JDK动态代理底层就是反射:
InvocationHandler handler = (proxy, method, args) -> {
    System.out.println("方法 " + method.getName() + " 被调用了");
    return method.invoke(target, args);
};
  • 序列化与反序列化
    JSON库(如Gson、Jackson)通过反射读取对象的字段,自动完成对象和JSON字符串的转换。
  • 热加载与插件化
    运行时从外部加载类文件,实现不重启程序更新功能。

六、反射的"代价"

反射很强大,但也有明显的缺点

缺点说明
性能损耗反射调用比直接调用慢10~100倍(JVM难以优化)
安全性问题setAccessible(true) 可以访问私有成员,破坏封装
编译期检查失效反射调用的方法名写错了,编译不会报错,运行时才抛异常
代码可读性差一堆字符串硬编码,IDE无法跳转,维护困难

使用建议:框架底层可以用,业务代码尽量别用。如果非用不可,做好缓存(Method、Field 对象可以复用)。

七、一个有趣的实验

用反射来"打破"String的不可变性:

String s = "Hello";
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
char[] value = (char[]) valueField.get(s);
value[0] = 'h';  // 改成小写
System.out.println(s);  // 输出: hello (理论上,但现代JDK有优化可能不生效)

⚠️ 警告:这只是实验!生产环境千万别这么干,String的不可变性是Java安全设计的基石。

八、总结

  反射是Java提供给程序员的"元能力"——让代码去操作代码本身。它是一把双刃剑
  用得好,能写出高度灵活、可扩展的框架
  用不好,会让代码变成难以维护的"黑魔法"
  作为学习者,理解反射的原理,是阅读Spring源码、理解动态代理、掌握AOP的必经之路。

到此这篇关于Java反射机制——运行时"透视"类的秘密的文章就介绍到这了,更多相关Java反射机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 纯Java类配置与@Configuration实战指南

    纯Java类配置与@Configuration实战指南

    本文介绍@Configuration的核心用法,实战@Bean定义Bean、@ComponentScan扫描组件、@Import组合配置类,带大家体验“无XML”的Spring配置新方式,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • Maven分步详解多环境配置与应用流程

    Maven分步详解多环境配置与应用流程

    这篇文章主要介绍了Maven进阶多环境配置与应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • SpringBoot MDC全链路调用日志跟踪实现详解

    SpringBoot MDC全链路调用日志跟踪实现详解

    这篇文章主要为大家介绍了SpringBoot MDC全链路调用日志跟踪实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • 学习Java的static与final关键字

    学习Java的static与final关键字

    本篇文章给大家详细分析了Java的static与final关键字知识点以及相关代码分享,有需要的读者跟着学习下吧。
    2018-03-03
  • java开发ServiceLoader实现机制及SPI应用

    java开发ServiceLoader实现机制及SPI应用

    这篇文章主要为大家介绍了java开发ServiceLoader实现机制及SPI应用,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Java轻松实现Excel转PDF的代码示例

    Java轻松实现Excel转PDF的代码示例

    在数据报告、财务报表等场景中,Excel转PDF的需求频繁出现,手动操作耗时且易出错,而依赖Office组件的方案又存在版本兼容性差、部署成本高等痛点,文将详解用 Spire.XLS for Java 实现Excel XLS 或 XLSX 文件到 PDF 的精准转换,需要的朋友可以参考下
    2025-08-08
  • Java后端Cookie实现(时间戳)代码实例

    Java后端Cookie实现(时间戳)代码实例

    这篇文章主要介绍了Java后端Cookie实现(时间戳)代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • 使用JPA自定义VO接收返回结果集(unwrap)

    使用JPA自定义VO接收返回结果集(unwrap)

    这篇文章主要介绍了使用JPA自定义VO接收返回结果集(unwrap),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • java中List对象排序通用方法

    java中List对象排序通用方法

    这篇文章主要介绍了java中List对象排序通用方法,涉及java针对List对象的操作技巧,需要的朋友可以参考下
    2015-05-05
  • java设计模式学习之装饰模式

    java设计模式学习之装饰模式

    这篇文章主要为大家详细介绍了java设计模式学习之装饰模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10

最新评论