当前位置: 首页 > news >正文

34. 什么是反射

34. 什么是反射

反射就是运行状态中,对于任意一个类都可以知道该类的属性和方法;对于任意的对象,都可以调用它的属性和方法。可以动态的获取类信息和对象的信息,这种功能称为反射机制。

反射的优缺点

优点:可以动态获取类的实例,提高灵活性,与动态编译结合,动态代理。

缺点:性能开销大,需要解析字节码。有解决方案:setAccessible(true)关闭JDK的安全检查来提升反射性能,多次创建一个类的实例时,有缓存会快很多。ReflectASM工具类,通过字节码生成的方式加快反射速度。

如何获取反射中的Class对象

  • Class.forName(“类的路径”) 类的全路径名

    Class<?> clazz = Class.forName("全类名");
    
    说明
    • 全类名:包括类的 包名 + 类名(例如 java.lang.String)。
    • 触发类的初始化:调用 Class.forName() 会触发类的 加载、链接和初始化(包括执行静态代码块)。
    • 适用场景:
      • 类名在运行时动态确定(例如从配置文件中读取类名)。
      • 需要动态加载类并执行其静态代码块。
    示例
    // 动态加载类(假设 Student 类在包 huang6 中)
    Class<?> clazz = Class.forName("huang6.Student");
    System.out.println(clazz); // 输出 class huang6.Student
    
  • 类名.Class方法

语法
Class<Student> clazz = Student.class;
说明
  • 编译期检查:在编译时就能检查类是否存在,更安全。

  • 不触发类的初始化:仅获取类的 Class 对象,不会执行静态代码块。

  • 适用场景

    • 类名在编译时已知。
    • 需要传递 Class 对象作为参数(例如泛型方法)。
示例
// 直接通过类名获取 Class 对象
Class<Student> clazz = Student.class;
System.out.println(clazz); // 输出 class huang6.Student
  • 对象.getClass()

    语法
    Class<? extends Student> clazz = student.getClass();
    
    说明
    • 通过实例对象获取:需要先创建类的实例对象。
    • 不触发类的初始化:类已经加载,因此不会执行静态代码块。
    • 适用场景:
      • 已有类的实例对象时(例如从集合或数据库中获取对象后)。
    示例
    // 通过实例对象获取 Class 对象
    Student student = new Student();
    Class<? extends Student> clazz = student.getClass();
    System.out.println(clazz); // 输出 class huang6.Student
    

Java反射API有几类

  • Class类

    • 作用:核心类,表示类或接口的类型信息

    • 功能:getName(),getSuperclass(),getInterfaces(),getConstructors(),getFields(),getMethods(),newInstance()或getDeclaredConstructor.newInstance()

    • 示例:

      Class<?> clazz = String.class;
      
  • Field类

    • 作用:类的字段信息,动态访问和修改

    • 功能:getName(),getType(),setAccessible(true)访问私有字段

      Field field = clazz.getDeclaredField("fieldName");
      field.setAccessible(true); // 突破访问权限限制
      field.set(instance, "newValue");
      
  • Method类

    • 作用:表示类的方法

    • 功能:getName(),getParameterTypes(),invoke()调用方法

      Method method = clazz.getMethod("methodName", paramTypes);
      method.invoke(instance, args);
      
  • Constructor类

    Constructor<?> constructor = clazz.getDeclaredConstructor(paramTypes);
    Object instance = constructor.newInstance(args);
    
  • Modifier类

  • Array类

  • Proxy 和 InvocationHandler

    • 作用:实现动态代理

      • 创建代理对象 Proxy.newProxyInstance()

      • 拦截方法调用 InvocationHandler的invoke方法

        InvocationHandler handler = (proxy, method, args) -> {System.out.println("方法调用前");return method.invoke(target, args);
        };
        Object proxy = Proxy.newProxyInstance(...);
        
  • 其他一些辅助类

注意事项

  1. 性能开销:反射操作比直接调用方法慢,避免在高性能场景频繁使用。
  2. 安全性:通过 setAccessible(true) 可以突破访问权限限制,但可能破坏封装性。
  3. 适用场景:常用于框架开发(如 Spring 依赖注入)、插件系统、通用工具类等。

反射使用的步骤:

  • 获取类的Class对象

    • Class.forName()
    • .Class
    • getClass()
  • 创建类的实例

    • 无参构造
      • Object instrance = clazz.getDeclaredConstructor().newInstance();
    • 有参构造
        1. Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
        1. Object instrance = constructor.newInstrance(“John”, 30);
      • newInstrance() 在Java9 标记为过时,推荐用Constructor.newInstrance()
  • 访问和修改字段

    • 获取字段

      • Field field = clazz.getDeclaredField("字段名称"); //获取指定字段(包括私有)
        field.setAccessible(true) // 突破访问权限限制(针对私有字段)
        
    • 读取字段值

      • Object value = field.get(instrance);
        
    • 修改字段值

      • field.set(instrance, "newvalue");
        
  • 调用方法

    • 获取方法

      • Method method = clazz.getDeclaredMethod("方法名", 参数.class);
        method.setAccessible(true);
        
    • 调用方法

      • Object result = method.invoke(instance,参数值)
        
    • 示例:

      // 调用 Person 类的 sayHello 方法
      Method sayHelloMethod = Person.class.getMethod("sayHello");
      sayHelloMethod.invoke(person);// 调用带参数的方法
      Method greetMethod = Person.class.getMethod("greet", String.class);
      greetMethod.invoke(person, "World");
      
  • 获取和调用构造方法

  • 获取类的元数据

  • 处理泛型和注解

为什么引入反射概念?反射机制的应用有哪些

引入反射最主要的是增强程序的动态性和灵活性。能够在代码运行时动态获取类的属性,方法,调用他们。核心目的

  • 动态性需求:
    • 一般情况下,都是在编译时就已经确定了程序的行为;但在某些场景下,需要动态获取类的属性或者方法。比如:配置文件的加载
  • 框架需求:
    • 像spring,可以利用反射机制来实现依赖注入,动态代理,注解处理
  • 兼容性与灵活性:
    • 反射可以绕过编译时的类型限制,可以处理未知的类和方法动态调用
  • 调试与测试:
    • 反射可以访问私有属性和方法,便于测试如Junit
  1. 动态加载类和调用方法

    • Class<?> clazz = Class.forName("com.example.MyClass");
      Object obj = clazz.getDeclaredConstructor().newInstance();
      Method method = clazz.getMethod("myMethod", String.class);
      method.invoke(obj, "参数");
      
  2. 注解处理

    • 如Junit的@Test,Spring的@Autowired

    • Class<?> clazz = Class.forName("com.example.MyClass");
      Object obj = clazz.getDeclaredConstructor().newInstance();
      Method method = clazz.getMethod("myMethod", String.class);
      method.invoke(obj, "参数");
      
  3. JDBC数据库连接

    • 通过Class.forName()加载驱动
  4. 动态代理和AOP编程

    • 日志,事务,权限校验等

    • InvocationHandler handler = (proxy, method, args) -> {System.out.println("Before method: " + method.getName());return method.invoke(target, args);
      };
      Object proxy = Proxy.newProxyInstance(...);
      

反射机制的原理是什么

Class actionClass = Class.forName("MyClass");
Object action = actionClass.newInstance();
Method method = actionClass.getMethod("MyMethod", null);
method.invoke(action, 参数);
  1. 反射会先获取类的实例,不会去交给Java去实现,而是交给了JVM去加载,获取ClassLoader,然后调用native方法获取信息,加载类就是回调Java.long.ClassLoader。最后,JVM又会回调ClassLoader进行类加载

  2. newInstance() 主要做了三件事

    • 权限检测,如果不通过就会抛出异常
    • 查询无参构造,并将其缓存起来
    • 调用具体方法的无参构造方法,生成实例并返回
  3. 获取Method对象

    • 在这里插入图片描述

    • 上面的Class对象是在加载类时由JVM构造的,JVM为每个类管理一个独一无二的Class对象,这份Class对象里维护着该类的所有Method,Field,Constructor的cache,这份cache也可以被称作根对象。每次getMethod获取到的Method对象都持有对根对象的引用,因为一些重量级的Method的成员变量(主要是MethodAccessor),我们不希望每次创建Method对象都要重新初始化,于是所有代表同一个方法的Method对象都共享着根对象的MethodAccessor,每一次创建都会调用根对象的copy方法复制一份

    • Method copy() {Method res = new Method(clazz, name, parameterTypes, returnType,exceptionTypes, modifiers, slot, signature,annotations, parameterAnnotations,annotationDefault);res.root = this;res.methodAccessor = methodAccessor;return res;}
      
  4. 调用invoke()方法
    在这里插入图片描述
    调用Method.invoke之后,会直接去调MethodAccessor.invoke。MethodAccessor就是上面提到的所有同名method共享的一个实例,由ReflectionFactory创建。


文章转载自:

http://KOaOe8tl.zthLn.cn
http://rHjDPtzh.zthLn.cn
http://zMSL0M2d.zthLn.cn
http://EcshMNjb.zthLn.cn
http://szwAVJqf.zthLn.cn
http://iXaqQqOm.zthLn.cn
http://t26tvR74.zthLn.cn
http://3HE3zvxC.zthLn.cn
http://3PU5OQfQ.zthLn.cn
http://LjEqKS67.zthLn.cn
http://gSM6Lt8m.zthLn.cn
http://uQTwnAzk.zthLn.cn
http://60gE5e5c.zthLn.cn
http://Oq0nJwaf.zthLn.cn
http://agXBMkgQ.zthLn.cn
http://ReGbDsyK.zthLn.cn
http://6X5SPEIn.zthLn.cn
http://06hZgEns.zthLn.cn
http://TMOiicbM.zthLn.cn
http://bld4jJb2.zthLn.cn
http://LF8YABbX.zthLn.cn
http://nbuXtenK.zthLn.cn
http://xURUiKJa.zthLn.cn
http://5aZAMQNq.zthLn.cn
http://jnQc38mc.zthLn.cn
http://RPZb8uMh.zthLn.cn
http://jRpcGmp7.zthLn.cn
http://tOW6rEIz.zthLn.cn
http://GKExtmRp.zthLn.cn
http://9yATIQVf.zthLn.cn
http://www.dtcms.com/a/372716.html

相关文章:

  • YOLO11 改进、魔改|通道自注意力卷积块CSA-ConvBlock,实现 “轻量化特征增强”
  • 优先搜索(DFS)实战
  • 计算机视觉opencv----银行卡号码识别
  • 第六章、从transformer到nlp大模型:编码器-解码器模型 (Encoder-Decoder)
  • pymodbus启动一个简单的modbus tcp server
  • 【NowCoder】牛客周赛 Round 108 EF (背包问题 | SOSDP)
  • 【ARMday02】
  • OFDR设备开机到出图的5个关键操作步骤
  • ArcGIS学习-19 实战-表面分析
  • 【算法】双指针(二)复写零
  • 视频串行解串器(SerDes)介绍
  • PyTorch 动态图的灵活性与实用技巧
  • 【P01_AI测试开发课程-导论】
  • 从社交破冰到学习规划,鸿蒙5开启智慧校园新生活
  • 【Linux操作系统】简学深悟启示录:文件fd
  • Kata Container 部署与应用实践
  • 【CentOS7】docker安装成功后测试,报Unable to find image ‘hello-world:latest‘ locally
  • springboot配置请求日志
  • 2-ATSAMV71Q21-BOOT
  • 【Qt开发】显示类控件(一)-> QLabel
  • 把不确定变成确定性收益:电力交易未来场景的预测、优化与实操
  • 大数据毕业设计选题推荐-基于大数据的国家药品采集药品数据可视化分析系统-Spark-Hadoop-Bigdata
  • 如何在Linux上使用Docker在本地部署开源PDF工具Stirling PDF:StirlingPDF+cpolar让专业操作像在线文档一样简单
  • 7,000 星!AutoMQ 开源再创里程碑
  • 四大金刚之计算机操作系统
  • 深入剖析 MyBatis 核心原理模块一:快速入门
  • 【Ansible】的介绍
  • VMware共享文件夹设置
  • YOLO11实战 第009期-基于yolo11的咖啡叶病害目标检测实战文档(yolo格式数据免费获取)
  • MATLAB可以实现的各种智能算法