JAVA Review-应用程序开发-JAVA反射机制(一)

前言

本节我们将学习一下JAVA的放射机制。

  • 什么是JAVA反射机制
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性、方法、构造器、包、父类信息等等;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。反射是框架设计的灵魂。

  • JAVA机制实现的一般步骤是什么?
    这里写图片描述
    图出处(https://blog.csdn.net/sinat_38259539/article/details/71799078

    • 创建一个JAVA 类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)。
    • JAVA 类的.class文件被加载到内存中之后(上图的new Student()方法),就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例。
    • 创建某个.class的实例,然后使用JAVA反射机制(Reflection API-java.lang.reflect )来获取该实例的属性、方法、构造器等等(此时还需注意属性、方法的作用域,后面我们将会讲解到)。
  • 使用JAVA反射机制我们能做什么?

    • 创建对应的运行时类的对象。
    • 获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、…)。
    • 调用对应的运行时类的指定的结构(属性、方法、构造器)。
    • 将反射机制应用于动态代理。
  • 什么场景下我们会使用JAVA 反射机制?

    • 逆向代码 ,例如反编译。
    • 与注解相结合的框架 例如Retrofit
    • 单纯的反射机制应用框架 例如EventBus 2.x
    • 动态生成类框架 例如Gson

概述

下面我们将从如何实现JAVA反射机制的一般步骤角度来学习以下JAVA反射机制。

  • 创建一个java类,包含一些常规的属性、方法(静态方法)、实现的接口、继承类(父类)、多种构造器等等。

    • Broker.java

      
      import java.sql.SQLDataException;
      
      public class Broker extends BrokerFamer implements BrokerForSalesInterface,BrokerForBankInterface {
      
          //常规私有属性
          private String area;
          private int zoom;
      
          //默认属性
          protected String nation;
          String company;
          //公有属性
          public String group;
      
          public String getArea() {
              return area;
          }
      
          public void setArea(String area) {
              this.area = area;
          }
      
          public int getZoom() {
              return zoom;
          }
      
          public void setZoom(int zoom) {
              this.zoom = zoom;
          }
      
          public String getNation() {
              return nation;
          }
      
          public void setNation(String nation) {
              this.nation = nation;
          }
      
          public String getCompany() {
              return company;
          }
      
          public void setCompany(String company) {
              this.company = company;
          }
      
          public String getGroup() {
              return group;
          }
      
          public void setGroup(String group) {
              this.group = group;
          }
      
          //常规方法
          public void callBroker(String area,Integer zoom){
              System.out.println("Call defaultBroker"+area+" within "+zoom+"zoom");
          }
      
          public void callBrokerWithoutParams(){
              System.out.println("Call defaultBroker without params");
          }
      
          //静态方法
          public static void callBrokerStatic(String area,Integer zoom){
              System.out.println("Call staticBroker"+area+" within "+zoom+"zoom");
          }
      
          //私有方法
          private void callBrokerPrivate(String area,Integer zoom)  throws SQLDataException{
              System.out.println("Call privateBroker"+area+" within "+zoom+"zoom");
          }
      
          //含有私有构造器的类无法被继承被实例化,因而本例子中讨论无意义.
          //常规无参构造器
          public Broker() {
              super();
              // TODO Auto-generated constructor stub
          }
          //常规无参构造器
          public Broker(String area,String nation){
              super();
              this.area = area;
              this.nation = nation;
          }
      
          @Override
          public String currenceForBank(String currence)  {
      
              return currence;
          }
      
          @Override
          public String currenceForSales(String currence) {
              // TODO Auto-generated method stub
              return currence;
          }
      
          @Override
          public String toString() {
              return "Broker [area=" + area + ", zoom=" + zoom + ", nation=" + nation + ", company=" + company + ", group="
                      + group + "]";
          }
      
      
      }
      
       - BrokerForBankInterface.java
      
      
          ```
          public interface BrokerForBankInterface {
      
              String currenceForBank(String currence);
          }
      
          ``` 
      
    • BrokerForSalesInterface.java

      public interface BrokerForSalesInterface {
      
          String currenceForSales(String currence);
      }

  • 获取运行时指定的Class对象

    /**
         * 创建并获取JAVA 运行时的class对象
         */
        @Test
        public void getClassInstance() throws Exception {
            // java中获取Class对象主要有三种实现方法:
            // 1.直接调用运行时类的.class属性
            Class clazz = Broker.class;
            System.out.println(clazz);
    
            //2.通过运行时类的对象调用起getClass方法
            Broker broker = new Broker();
            Class clazz1 = broker.getClass();
            System.out.println(clazz1);
    
            //3.调用Class的静态方法:forName
            String className =  "coreJavaReview.reflect.Broker" ;
            Class clazz2 = Class.forName(className);
            System.out.println(clazz2);
        }

  • 获取并调用对应的运行时类的完整结构

    • 属性

      • 属性

        /**
             * 通过获取运行时Class对象获取属性
             */
            @Test
            public void getBrokerFielsInfo(){
                //子类中/父类的Public修饰的属性
                Class clazz = Broker.class;
                Field[] fields = clazz.getFields();
                for(int i = 0;i < fields.length;i++){
                    System.out.println(fields[i]);
                }
                //只获取父类中Public修饰的属性
                /*Class superClazz = clazz.getSuperclass();
                Field[] supeFields = superClazz.getFields();
                for(int i = 0;i < supeFields.length;i++){
                    System.out.println(supeFields[i]);
                }*/
                System.out.println();
        
                //2.getDeclaredFields():获取运行时类本身声明的所有的属性
                Field[] fields1 = clazz.getDeclaredFields();
                for(Field f : fields1){
                    System.out.println(f.getName());
                }
        
                //只获取父类中所有的属性
                /*System.out.println();
                Class superClazz = clazz.getSuperclass();
                Field[] superFields = superClazz.getDeclaredFields();
                for(Field f : superFields){
                    System.out.println(f.getName());
                }*/
        
            }

        1、获取父类属性时需要调用 Class superClazz = clazz.getSuperclass();
        2、getFields()方法只能获取子类或父类中被public 修饰的属性,其他类型的属性需要使用getDeclaredFields()来强制获取

      • 获取权限的权限修饰符 变量类型 变量名

        /**
             * 获取属性的各种属性
             */
            @Test
            public void getBrokerFieldsProperty(){
                Class clazz = Broker.class;
                Field[] fields1 = clazz.getDeclaredFields();
                for(Field f : fields1){
                    //1.获取每个属性的权限修饰符
                    int i = f.getModifiers();
                    String str1 = Modifier.toString(i);
                    System.out.print(str1 + " ");
                    //2.获取属性的类型
                    Class type = f.getType();
                    System.out.print(type.getName() + " ");
                    //3.获取属性名
                    System.out.print(f.getName());
        
                    System.out.println();
                }
            }
      • 调用指定的属性

        /**
             * 调用指定Clazz的属性
             */
            @Test
            public void getBrokerFieldsInvok() throws Exception{
                Class clazz = Broker.class;
                //1.获取指定的属性
                //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
                Field name = clazz.getField("group");
                //2.创建运行时类的对象 
                Broker broker = (Broker)clazz.newInstance();
                System.out.println(broker);
                //3.将运行时类的指定的属性赋值
                name.set(broker,"Dustyone");
                System.out.println(broker);
                System.out.println("%"+name.get(broker));
        
                System.out.println();
                //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
                Field zoom = clazz.getDeclaredField("zoom");
                //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
                zoom.setAccessible(true);
                zoom.set(broker,10);
                System.out.println(broker);
            }

        1、没有被public修饰的字段需要使用 getDeclaredField()调用并且使用 zoom.setAccessible(true)方法来获取权限。建议在获取和调用Class对象的属性是统一使用 getDeclaredField(),然后使用 setAccessible 统一授权。

  • 方法

    • 方法

      /**
           * 获取Class对象的方法
           * 
           */
          @Test
          public void getBrokerMethod(){
              Class clazz = Broker.class;
              //1.getMethods():获取运行时类及其父类中所有的声明为public的方法
              Method[] m1 = clazz.getMethods();
              for(Method m : m1){
                  System.out.println(m);
              }
              System.out.println();
      
              //2.getDeclaredMethods():获取运行时类本身声明的所有的方法
              Method[] m2 = clazz.getDeclaredMethods();
              for(Method m : m2){
                  System.out.println(m);
              }
          }
    • 获取方法属性

      /**
           * 获取方法属性
           * 
           */
          @Test
          public void getBrokeMethodProperty(){
              Class clazz = Broker.class; 
      
              Method[] m2 = clazz.getDeclaredMethods();
              for(Method m : m2){
                  //1.注解
                  Annotation[] ann = m.getAnnotations();
                  for(Annotation a : ann){
                      System.out.println(a);
                  }
      
                  //2.权限修饰符
                  String str = Modifier.toString(m.getModifiers());
                  System.out.print(str + " ");
                  //3.返回值类型
                  Class returnType = m.getReturnType();
                  System.out.print(returnType.getName() + " ");
                  //4.方法名
                  System.out.print(m.getName() + " ");
      
                  //5.形参列表
                  System.out.print("(");
                  Class[] params = m.getParameterTypes();
                  for(int i = 0;i < params.length;i++){
                      System.out.print(params[i].getName() + " args-" + i + " ");
                  }
                  System.out.print(")");
      
                  //6.异常类型
                  Class[] exps = m.getExceptionTypes();
                  if(exps.length != 0){
                      System.out.print("throws ");
                  }
                  for(int i = 0;i < exps.length;i++){
                      System.out.print(exps[i].getName() + " ");
                  }
                  System.out.println();
              }
          }
    • 调用方法

      /**
       * 调用方法
       */
      @Test
      public void getBrokeMethodInvok() throws Exception{
          Class clazz = Broker.class;
          //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
          Method m1 = clazz.getMethod("callBroker",String.class,Integer.class);
          Broker broker = (Broker)clazz.newInstance();
          //调用指定的方法:Object invoke(Object obj,Object ... obj)
          Object returnValue1 = m1.invoke(broker,"Europe",1457);//
          System.out.println(returnValue1);
      
          Method m2 = clazz.getMethod("toString");
          Object returnVal1 = m2.invoke(broker);
          System.out.println(returnVal1);//
          //对于运行时类中静态方法的调用
          Method m3 = clazz.getMethod("callBrokerStatic",String.class,Integer.class);
          m3.invoke(Broker.class,"Asia",17541);
      
          //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
          Method m4 = clazz.getDeclaredMethod("callBrokerPrivate",String.class,Integer.class);
          m4.setAccessible(true);
          Object value = m4.invoke(broker,"Africa",14120);//
          System.out.println(value);//10
      }
  • 构造器

    /**
     * 获取构造器属性
     * @throws Exception
     */
    @Test
    public void getBrokerConstructorProperty() throws Exception {
        Class clazz = Broker.class;
        Constructor[] constr = clazz.getConstructors();
        for (Constructor constructor : constr) {
            System.out.println(constructor.getName() + "paramsCount" + constructor.getParameterCount() + "params"
                    + constructor.getParameters());
            ;
        }
    
        //获取父类构造器信息
        Class superClazz = clazz.getSuperclass();
        Constructor<BrokerFamer> [] surConstr = superClazz.getConstructors();
        for (Constructor superConstructor : surConstr) {
            System.out.println(superConstructor.getName() + "paramsCount" + superConstructor.getParameterCount() + "params"
                    + superConstructor.getParameters());
            ;
        }
    }
  • 父类信息(属性、方法、构造器等)
    参考以上信息

  • 实现类信息

    • 获取注解

      /**
           * 获取Class对象中的所有注解信息
           * @throws Exception
           */
          @Test
          public void getBrokerAnnotations() throws Exception{
              Class clazz = Broker.class;
              Annotation[] anns = clazz.getAnnotations();
              for(Annotation a : anns){
                  System.out.println(a);
              }
          }
    • 获取所在包信息

      /**
           * 获取Class对象的所在包信息
           * @throws Exception
           */
          @Test
          public void getBrokerPacageInfo() throws Exception{
              Class clazz = Broker.class;
              Package packageInfo = clazz.getPackage();
              System.out.println(packageInfo);
          }
    • 获取实现接口类信息

      /**
           * 获取Class对象实现接口信息
           * @throws Exception
           */
          @Test
          public void getBrokerInterface() throws Exception{
              Class clazz = Broker.class;
              Class[] interfaces = clazz.getInterfaces();
              for(Class interfaceInfo : interfaces){
                  System.out.println(interfaceInfo);
              }
          }
  • …..

小结

  • 必须在JAVA程序运行或者应用程序运行时才能动态地获取指定Class实例。(java文件通过javax.exe编译、java.exe加载之到内存之后)。
  • 获取指定的Class实例之后可以使用reflect API 来获取指定Class。并且要想获取非public修饰的属性或方法时需要额外的获取权限处理。
  • 通过反射机制调用类的方法时需要实例化Class对象如 Broker broker = (Broker)clazz.newInstance()。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值