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语言的一项重要特性,在开发复杂应用程序、框架等场景中发挥着关键作用,但使用时需要权衡其带来的灵活性与性能、安全性等方面的问题。

相关推荐
探索java6 分钟前
Tomcat Server 组件原理
java·后端·tomcat
勿在浮沙筑高台6 分钟前
无法获取实体类com.example.springdemo2.entity.po.UserPO对应的表名!
java·spring boot·mybatis
程高兴8 分钟前
遗传算法求解冷链路径优化问题matlab代码
开发语言·人工智能·matlab
wow_DG12 分钟前
【C++✨】多种 C++ 解法固定宽度右对齐输出(每个数占 8 列)
开发语言·c++·算法
用户83562907805126 分钟前
Java使用Spire.Doc实现Word转PDF:格式精准的自动化解决方案
java
陆小叁38 分钟前
基于Flink CDC实现联系人与标签数据实时同步至ES的实践
java·elasticsearch·flink
CHEN5_0240 分钟前
【Java基础】反射,注解,异常,Java8新特性,object类-详细介绍
java·开发语言
Cx330❀1 小时前
【数据结构初阶】--排序(四):归并排序
c语言·开发语言·数据结构·算法·排序算法
云间月13141 小时前
飞算JavaAI智慧文旅场景实践:从景区管理到游客服务的全链路系统搭建
java·开发语言
盖世英雄酱581361 小时前
必须掌握的【InheritableThreadLocal】
java·后端