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

相关推荐
二哈喇子!1 小时前
若依【(前后端分离版)SpringBoot+Vue3】
java·spring boot·后端
一只小风华~2 小时前
JavaScript 函数
开发语言·前端·javascript·ecmascript·web
苕皮蓝牙土豆2 小时前
Qt 分裂布局:QSplitter 使用指南
开发语言·qt
Monkey-旭4 小时前
Android Handler 完全指南
android·java·handler
秃狼4 小时前
Execel文档批量替换标签实现方案
java
Brookty5 小时前
Java线程安全与中断机制详解
java·开发语言·后端·学习·java-ee
Sylvia-girl5 小时前
排序查找算法,Map集合,集合的嵌套,Collections工具类
java·算法·排序算法
TT哇5 小时前
【分治】归并排序——排序数组(medium)
java·算法·排序算法
给力学长5 小时前
自习室预约小程序的设计与实现
java·数据库·vue.js·elementui·小程序·uni-app·node.js
试着5 小时前
零基础学习性能测试第五章:JVM性能分析与调优-JVM概念,java程序运行原理
java·jvm·学习·零基础·性能测试