一.定义
在Java中,反射(Reflection) **是指程序在运行时能够获取自身信息,并动态操作类或对象的机制。**简单来说,就是让代码"看透"自身的结构,比如类的属性、方法、构造器等,并能在运行中灵活调用它们,无需在编译时就确定具体操作。
* 反射式获取类信息的能力*
为什么需要反射?
反射打破了Java的编译时类型检查限制,让程序更灵活。比如开发框架(如Spring的IOC容器)需要动态创建对象、调用方法,此时无法在编译期确定具体类,反射就成了核心工具。
反射的核心操作
- 获取类对象:通过 Class.forName("类全路径")源文件阶段 、 对象.getClass() 或 类名.class 获取 Class 对象,这是反射的入口。

-
操作构造器:通过 Class 对象的 getConstructor() 等方法获取构造器,动态创建实例(如 constructor.newInstance() )。
-
操作方法:用 getMethod() 获取方法,通过 method.invoke(对象, 参数) 动态调用。
-
操作属性:用 getField() 获取字段,通过 field.set(对象, 值) 修改属性(即使是私有属性,也可通过 setAccessible(true) 突破访问限制)。
为什么他们的地址会是一样的呢?
这是他们是用三种上述方式获取Student类,由于java数据加载(就是加载方法,这里是Class)只会有一次 ,就是说上面的流程图只会走一次,即每个阶段只会有一个类对象创建出来,然后留下,流程接着走,所以地址一样(三个阶段可以想象成一个人不同年龄段,但是始终是一个人,所以地址不变)

二.获取数据
由下图可知clazz.getDeclaredFields()方法可以获取到全部类型的信息
而clazz.getFields()方法只能调到public类里的信息
1. 那麽该如何获取变量呢?
java
Field nameField = clazz.getDeclaredField("name");
System.out.println(nameField);
Field ageField = clazz.getField("height");
System.out.println(ageField);
第二行会报错,因为getField只能调到public修饰的数据
2./如何获取方法呢?
跟对象一样,方法换为getDeclaredMethod()即可,同样加不加Declare的区别也和对象一样
3.如何获取指定方法而不是全部方法呢?
如下的格式,注意如果方法里带参数,调用时也要带上参数

4.构造函数
格式如下,Delared加不加与上面的规则一样,getDeclaredConstructors()方法
上面是获取全部的函数(用数组形式输出的),下面是指定的函数

也可如下调用函数,注意暴力反射,invoke()里面要有对象和对象对应的值

5. 构造器(设置值取值)
上面的是模板,下面的是举例,注意必须改后要复值

但是对于pravite的值来说就没有这么简单,如果在向上面那样转的话,会报错误,那该怎么办?
这时候就需要用到"暴力反射",只需要在需要改的构造器上加上名称.setAccee\ssible(true)即可
java
Constructor declaredConstructor1 = clazz.getDeclaredConstructor(String.class, int.class, int.class);
//暴力反射------->private
declaredConstructor1.setAccessible(true);
Student student2 = (Student) declaredConstructor1.newInstance("张三", 18, 180);
也可以如下写(简单一点,但是set里要对象,需要创建如上面的student1)注意暴力反射

(1).暴力反射
定义 :在Java的反射机制中,"暴力反射" 通常指的是利用反射突破Java语言本身的访问控制修饰符限制,去访问和操作类中原本受访问权限限制的成员(如私有构造函数、私有字段、私有方法
举例:
java
class PrivateConstructorClass {
private PrivateConstructorClass() {
System.out.println("私有构造函数被调用");
}
}
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = PrivateConstructorClass.class;
// 获取私有构造函数
java.lang.reflect.Constructor<?> constructor = clazz.getDeclaredConstructor();
// 设置可访问,即暴力反射
constructor.setAccessible(true);
// 调用私有构造函数创建对象
PrivateConstructorClass instance = (PrivateConstructorClass) constructor.newInstance();
}
}
在上述代码中,通过 setAccessible(true) 来设置私有构造函数可访问,进而创建对象,这打破了原本Java访问权限对私有构造函数的限制。
暴力反射的风险与应用场景:
-
风险:破坏了Java的封装性,可能导致代码逻辑混乱,安全性降低。例如,随意修改私有字段可能会让对象处于不合理的状态,破坏程序的稳定性。
-
应用场景:在一些框架开发(如Spring、Hibernate等)中,需要动态地创建对象、访问和修改对象属性或调用方法,在无法提前预知具体类结构的情况下,会使用暴力反射来实现框架的功能。
三.总结·
反射的主要操作
- 获取 Class 对象
java
- 类名.class : Class<?> clazz = String.class; 。
- 对象.getClass() : String str = "hello"; Class<?> clazz = str.getClass(); 。
- Class.forName("全类名") : Class<?> clazz = Class.forName("java.lang.String"); 。
- 创建对象
java
- 通过无参构造函数: Class<?> clazz = MyClass.class; MyClass obj = (MyClass) clazz.getConstructor().newInstance();
- 通过有参构造函数:先获取构造函数对象,再传入对应参数创建对象,如 Constructor<?> constructor = clazz.getConstructor(int.class, String.class); MyClass obj = (MyClass) constructor.newInstance(1, "test");
- 访问和修改属性
java
- 获取属性: Field field = clazz.getDeclaredField("fieldName"); , getDeclaredField 获取类中声明的所有属性(包括私有), getField 获取类中公共的属性(包括从父类继承的)。
- 修改属性:对于私有属性,需先 field.setAccessible(true); ,再 field.set(obj, newValue); 。
- 调用方法
java
获取方法: Method method = clazz.getDeclaredMethod("methodName", 参数类型.class);
- 调用方法:对于私有方法,先 method.setAccessible(true); ,再 method.invoke(obj, 参数值);
应用场景
-
框架开发:如Spring框架中,使用反射实现依赖注入(DI)和面向切面编程(AOP),动态创建和管理Bean实例;Hibernate通过反射来操作实体类与数据库表之间的映射关系。
-
插件系统:允许程序在运行时加载外部插件,通过反射调用插件中的类和方法,实现功能扩展。
-
序列化与反序列化:像Java内置的序列化机制,以及JSON序列化库(如Jackson、Gson )在将对象转换为字节流或JSON字符串,以及反向转换时,会借助反射获取对象的属性信息。
-
单元测试框架:JUnit等测试框架利用反射来查找和运行标记为测试的方法。
优点与缺点
-
优点
-
高度灵活性:能够在运行时动态处理不同的类,适应变化的需求,增强程序的扩展性。
-
解耦代码:在框架设计中,降低模块之间的耦合度,提高代码的可维护性和可复用性。
-
缺点
-
性能开销:反射操作涉及到动态解析,比直接调用方法和访问属性要慢,频繁使用可能影响程序性能。
-
破坏封装性:通过反射可以访问私有成员,破坏了Java语言的封装特性,可能导致代码难以理解和维护,也带来一定的安全风险。
总之,反射是Java语言的一项重要特性,在开发复杂应用程序、框架等场景中发挥着关键作用,但使用时需要权衡其带来的灵活性与性能、安全性等方面的问题。