java之反射

什么是反射?

  • 概念
    • 反射 : 将类的各个组成部分封装为其他对象,这就是反射机制
    • 三个阶段 : Java代码在计算机中经历的三个阶段
      • Source : 源代码阶段,存在于物理地址中(比如.java文件,.class文件)
      • Class : 类对象阶段,类加载器将源代码加载进内存,将类的各个组成部分封装为其他对象
      • Runtime : 运行时阶段,通过Class对象创建对象
  • 好处 :
    1.可以在程序运行过程中,操作对象。
    2.可以解耦,提高程序的可扩展性。
  • 获取Class对象
    • Class.forName(“全类名”) : 将字节码文件加载进内存,返回Class对象
      • 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
    • 类名.class : 通过类名的属性class获取
      • 多用于参数的传递
    • 对象.getClass() : 根类Object中的方法
      • 多用于对象的获取字节码的方式
  • Class对象的功能
    • 获取成员变量们:
      • Field[] getFields() :获取所有public修饰的成员变量
      • Field getField(String name) 获取指定名称的 public修饰的成员变量
      • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
      • Field getDeclaredField(String name) 获取指定名称的 成员变量,不考虑修饰符
    • 获取构造方法们:
      • Constructor<?>[] getConstructors()
      • Constructor getConstructor(类<?>… parameterTypes)
      • Constructor getDeclaredConstructor(类<?>… parameterTypes)
      • Constructor<?>[] getDeclaredConstructors()
    • 获取成员方法们:
      • Method[] getMethods()
      • Method getMethod(String name, 类<?>… parameterTypes)
      • Method[] getDeclaredMethods()
      • Method getDeclaredMethod(String name, 类<?>… parameterTypes)
  • 封装对象的功能
    • Field:成员变量
      • 设置值
        • void set(Object obj, Object value)
      • 获取值
        • get(Object obj)
      • 忽略访问权限修饰符的安全检查
        • setAccessible(true):暴力反射
    • Constructor:构造方法
      • 创建对象:
        • T newInstance(Object… initargs)
        • 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance()方法
    • Method:方法对象
      • 执行方法:
        • Object invoke(Object obj, Object… args)
      • 获取方法名称:
        • String getName:获取方法名

反射的应用

  • 动态代理
    • 特点 : 字节码随用随创建,随用随加载。它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载
    • 类型
      • 基于接口的动态代理:
        • 提供者:JDK 官方的 Proxy 类。
        • 要求:被代理类最少实现一个接口。
      • 基于子类的动态代理:
        • 提供者:第三方的 CGLib
        • 要求:被代理类不能用 final 修饰的类(最终类)。
    • 区别:
    1. 前者需要接口,后者不需要接口
    2. 前者, 代理对象与被代理对象是兄弟关系,
      后者, 代理对象与被代理对象是父子关系, 代理对象继承了被代理对象。代理对象是子,被代理对象是父。
    • 代码示例
      基于接口的动态代理:
      /**
      *	被代理对象接口
      **/
      public interface IProducer {
      
          /**
           * 销售
           * @param money
           */
          public void saleProduct(float money);
      
          /**
           * 售后
           * @param money
           */
          public void afterService(float money);
      
      }
      -------------------------------------------------------------
      /**
      *	被代理对象
      **/
      public class Producer implements IProducer{
      
          /**
           * 销售
           * @param money
           */
          @Override
          public void saleProduct(float money){
              System.out.println("销售产品,并拿到钱:"+money);
          }
      
          /**
           * 售后
           * @param money
           */
          @Override
          public void afterService(float money){
              System.out.println("提供售后服务,并拿到钱:"+money);
          }
      
      }
      ------------------------------------------------------------------
      public void dynamicProxyOne(){
          final Producer producer = new Producer();
          /**
           *  基于接口的动态代理:
           *      涉及的类:Proxy
           *      提供者:JDK官方
           *  如何创建代理对象:
           *      使用Proxy类中的newProxyInstance方法
           *  创建代理对象的要求:
           *      被代理类最少实现一个接口,如果没有则不能使用
           *  newProxyInstance方法的参数:
           *      ClassLoader:类加载器
           *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
           *      Class[]:字节码数组
           *          它是用于让代理对象和被代理对象有相同方法。固定写法。
           *      InvocationHandler:用于提供增强的代码
           *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
           *          此接口的实现类都是谁用谁写。
           */
          IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(
                  producer.getClass().getClassLoader(),
                  producer.getClass().getInterfaces(),
                  new InvocationHandler() {
                      /**
                       * 作用:执行被代理对象的任何接口方法都会经过该方法
                       * 方法参数的含义
                       * @param proxy   代理对象的引用
                       * @param method  当前执行的方法
                       * @param args    当前执行方法所需的参数
                       * @return        和被代理对象方法有相同的返回值
                       * @throws Throwable
                       */
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          //提供增强的代码
                          Object returnValue = null;
      
                          //1.获取方法执行的参数
                          Float money = (Float)args[0];
                          //2.判断当前方法是不是销售
                          if("saleProduct".equals(method.getName())) {
                              returnValue = method.invoke(producer, money*0.8f);
                          }
                          return returnValue;
                      }
                  });
          proxyProducer.saleProduct(10000f);
      }
      
      基于子类的动态代理:
      /**
      * 被代理对象
      **/
      public class Producer{
      
          /**
           * 销售
           * @param money
           */
          public void saleProduct(float money){
              System.out.println("销售产品,并拿到钱:"+money);
          }
      
          /**
           * 售后
           * @param money
           */
          public void afterService(float money){
              System.out.println("提供售后服务,并拿到钱:"+money);
          }
      }
      ------------------------------------------------------------------------
      public void dynamicProxyTwo(){
          final Producer producer = new Producer();
      
              /**
               *  基于子类的动态代理:
               *      涉及的类:Enhancer
               *      提供者:第三方cglib库
               *  如何创建代理对象:
               *      使用Enhancer类中的create方法
               *  创建代理对象的要求:
               *      被代理类不能是最终类
               *  create方法的参数:
               *      Class:字节码
               *          它是用于指定被代理对象的字节码。
               *
               *      Callback:用于提供增强的代码
               *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
               *          此接口的实现类都是谁用谁写。
               *          我们一般写的都是该接口的子接口实现类:MethodInterceptor
               */
              Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
                  /**
                   * 执行被代理对象的任何方法都会经过该方法
                   * @param proxy
                   * @param method
                   * @param args
                   *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
                   * @param methodProxy :当前执行方法的代理对象
                   * @return
                   * @throws Throwable
                   */
                  @Override
                  public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                      //提供增强的代码
                      Object returnValue = null;
      
                      //1.获取方法执行的参数
                      Float money = (Float)args[0];
                      //2.判断当前方法是不是销售
                      if("saleProduct".equals(method.getName())) {
                          returnValue = method.invoke(producer, money*0.8f);
                      }
                      return returnValue;
                  }
              });
              cglibProducer.saleProduct(12000f);
      }
      

反射相关的FQA

  • 列举5个反射过程中使用的API
    • getClass() , newInstance() , getFileds() , getConstructors(), getMethods
  • 反射的优缺点
    • 优点: 可以在程序运行过程中,操作对象。可以解耦,提高程序的可扩展性。
    • 缺点: 对性能有影响,这类操作总是慢于直接执行java代码。
  • 哪些地方使用到了反射?
    1. JDBC中,利用反射动态加载了数据库驱动程序。
    2. Web服务器中利用反射调用了Sevlet的服务方法。
    3. Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
    4. 很多框架都用到反射机制,注入属性,调用方法,如Spring。

  关于反射相关的知识点就先说道这里,还有很多需要学习的地方,欢迎大家纠正和补充💛

参考

反射常见面试题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值