JAVA中的反射

一.定义

在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等)中,需要动态地创建对象、访问和修改对象属性或调用方法,在无法提前预知具体类结构的情况下,会使用暴力反射来实现框架的功能。

三.总结·

反射的主要操作

  1. 获取 Class 对象
java 复制代码
-  类名.class : Class<?> clazz = String.class;  。
-  对象.getClass()  : String str = "hello"; Class<?> clazz = str.getClass();  。
-  Class.forName("全类名")  : Class<?> clazz = Class.forName("java.lang.String");  。
  1. 创建对象
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"); 
  1. 访问和修改属性
java 复制代码
- 获取属性: Field field = clazz.getDeclaredField("fieldName");  , getDeclaredField 获取类中声明的所有属性(包括私有), getField 获取类中公共的属性(包括从父类继承的)。
- 修改属性:对于私有属性,需先 field.setAccessible(true);  ,再 field.set(obj, newValue);  。
  1. 调用方法
java 复制代码
 获取方法: Method method = clazz.getDeclaredMethod("methodName", 参数类型.class); 
- 调用方法:对于私有方法,先 method.setAccessible(true);  ,再 method.invoke(obj, 参数值); 
 

应用场景

  1. 框架开发:如Spring框架中,使用反射实现依赖注入(DI)和面向切面编程(AOP),动态创建和管理Bean实例;Hibernate通过反射来操作实体类与数据库表之间的映射关系。

  2. 插件系统:允许程序在运行时加载外部插件,通过反射调用插件中的类和方法,实现功能扩展。

  3. 序列化与反序列化:像Java内置的序列化机制,以及JSON序列化库(如Jackson、Gson )在将对象转换为字节流或JSON字符串,以及反向转换时,会借助反射获取对象的属性信息。

  4. 单元测试框架:JUnit等测试框架利用反射来查找和运行标记为测试的方法。

优点与缺点

  • 优点

  • 高度灵活性:能够在运行时动态处理不同的类,适应变化的需求,增强程序的扩展性。

  • 解耦代码:在框架设计中,降低模块之间的耦合度,提高代码的可维护性和可复用性。

  • 缺点

  • 性能开销:反射操作涉及到动态解析,比直接调用方法和访问属性要慢,频繁使用可能影响程序性能。

  • 破坏封装性:通过反射可以访问私有成员,破坏了Java语言的封装特性,可能导致代码难以理解和维护,也带来一定的安全风险。

总之,反射是Java语言的一项重要特性,在开发复杂应用程序、框架等场景中发挥着关键作用,但使用时需要权衡其带来的灵活性与性能、安全性等方面的问题。

相关推荐
zhangfeng11332 小时前
openclaw skills 小龙虾技能 通讯仿真 matlab skill Simulink Agentic Toolkit,通过kimi找到,mcp通讯
开发语言·matlab·openclaw·通讯仿真
Javatutouhouduan8 小时前
2026Java面试的正确打开方式!
java·高并发·java面试·java面试题·后端开发·java编程·java八股文
chao1898448 小时前
基于 SPEA2 的多目标优化算法 MATLAB 实现
开发语言·算法·matlab
JAVA面经实录9178 小时前
Java初级最终完整版学习路线图
java·spring·eclipse·maven
赏金术士8 小时前
Kotlin 习题集 · 高级篇
android·开发语言·kotlin
Cat_Rocky9 小时前
k8s-持久化存储,粗浅学习
java·学习·kubernetes
楼兰公子9 小时前
buildroot 在编译rust时裁剪平台类型数量的方法
开发语言·后端·rust
知识领航员10 小时前
蘑兔AI音乐深度实测:功能拆解、实测表现与适用场景
java·c语言·c++·人工智能·python·算法·github
吴声子夜歌10 小时前
Go——并发编程
开发语言·后端·golang
释怀°Believe10 小时前
Spring解析
java·后端·spring