zoukankan      html  css  js  c++  java
  • 类型信息(RTTI和反射)——反射

    运行时类型信息可以让你在程序运行时发现和使用类型信息。

    在Java中运行时识别对象和类的信息有两种方式:传统的RTTI,以及反射。下面就来说说反射。

    重点说说通过反射获取方法以及调用方法,即类方法提取器

    1、反射:

      如果你不知道一个对象的确定类型,RTTI可以告诉你。但是有个限制:这个类型在编译期间必须已知,才能使用RTTI来识别它。例如,在磁盘上或者网络中的一段字符串,被告知代表一个类,但是编译器在编译代码的时候,并不知道,怎么才能使用这个类呢?这时候就需要使用反射。

      通过反射与未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(与RTTI一样)。但在这之后,在做其它事情之前,必须加载那个类的 Class 对象。因此,那个类的.class 文件对于 JVM 来说必须是可获取的,要么在本地机器上,要么可以通过网络取得。 

           RTTI:编译器在编译期间打开和检查.class文件

           反射:编译器在运行期间打开和检查.class文件

    1.1类方法提取器

     1.1.1获取方法

    步骤:

    1. 先找到方法所在类的字节码 
    2. 找到需要被获取的方法

    Class类中获取方法:

    public Method[] getMethods();//获取包括自身和继承(实现)过来的所有的public方法——Method不支持泛型<>,即后面不接<>
    
    public Method[] getDeclaredMethods();//获取自身所有的方法(private、public、protected,和访问权限无关),不包括继承的
    
    public Method[] getMethod(String methodName, Class<T>...parameterTypes);//表示获取指定的一个公共的方法,包括继承的
    
    public Method[] getDeclaredMethod(String methodName, Class<T>...parameterTypes);//表示获取本类中的一个指定的方法(private、protected、public,与访问权限无关),不包括继承的方法

    其中参数: methodName:表示获取的方法的名字

                       parameterTypes:表示获取的方法的参数的Class类型

    1.1.2调用方法

     步骤:

    1. 先找到方法所在类的字节码

    2. 找到需要被获取的方法

    3. 调用该方法
    class User{
    
      public void sayHello(){...}
    
      public void sayHi(String naem){...}
    
      private  void sayGoodBye(String name, int age){...}
    
    }

    如何使用反射调用一个方法?

    在Method类中有一个方法:

    public Object invoke(Object  obj,  Object...  args);//表示调用当前Method所表示的方法
    

    参数:  obj: 表示被调用方法底层所属对象

                 args: 表示调用方法时传递的实际参数

         返回:方法调用后,底层方法的返回结果

    Class<User> clz=User.class;
    
    Method mt=clz.getMethod(“sayHi”, String.class);
    
    Object obj=clz.newInstance();
    
    Object ret=mt.invoke(obj, “wili”);//要调用实例方法,必须有一个对象,方法的底层对象就是指当前Method所在的类的实例对象,sayHi方法具有返回值,调用该方法后的返回结果使用Object接收

    如何调用私有方法?

    Method mt=clz.getDeclaredMethod(“sayGoodBye”, String.class, int.class);
    
    //在调用私有方法之前,需设置该方法为可访问的权限:——否则会报错
    
    mt.setAccessible(true);
    
    mt.invoke(clz.newInstance(), “limi”, 17);

    如何调用静态方法?

    class User{
    
        public static void staticMethod(){
    
           System.out.println(“static mthod invoke.”);
    
        }
    
    }
    Class<User> clz=User.class;
    
    Method staticMethod=clz.getMethod(“staticMthod”);
    
    //两种方式调用静态方法:
    
    //1. 因为静态方法属于所有实例对象公共的,可以创建该类的一个任意对象,通过该对象调用
    
     staticMethod.invoke(clz.newInstance());//staticMethod无参,故参数列表类型不填
    
    //2. 如果底层方法是静态的,那么可以忽略指定的obj参数,将obj参数设置为null即可
    
    staticMethod.invoke(null);

    如何调用可变参数的方法?

    class User{
    
        public static int sum(int[] ages){
    System.out.println(args);
    //打印结果可看出:可变参数底层就是一个数组 int sum=0;
      
    for(int i : args){ sum+=i; } return sum; } public static void show(String[] args){...} }
    Class<User> clz=User.class;
    
    Method m=clz.getMethod(“sum”, int[].class);//可变参数底层就是一个数组
    
    M.invoke(null,  new int[]{1,2,3});
    
    Method m=clz.getMethod(“show”, String[].class);
    
    //M.invoke(null,  new String[]{“A”,”B”,”C”});//会报错,可变参数是引用类型时,底层会自动解包,上述调用被解包后变成M.invoke(null,“A”,”B”,”C”);——为了解决该问题,我们再使用一层数组把实际参数包装起来
    
    M.invoke(null,  new Object[]{new String[]{“A”,”B”,”C”}});//正确

    通用方法:

    以后在使用反射调用invoke方法时,在传递实际参数的时候,无论是基本数据类型,还是引用类型,或者是可变参数类型,把实际参数都包装在一维数组中。

    m.invoke(方法的底层对象,new Object[]{实际参数});

    m.invoke(nullnew Object[]{17});//方法参数为基本类型,且只有一个参数,解包后变成m.invoke(null,17});
    
    m.invoke(nullnew Object[]{“xxx”});//方法参数为String类型,且只有一个参数
    
    m.invoke(nullnew Object[]{new int[]{1,2}});//方法参数为int类型,且为可变参数或者数组类型
    
    m.invoke(nullnew Object[]{new String[]{“A”,”B”}});//方法参数为String类型,且为可变参数或者数组类型,new String[]{“A”,”B”}为传递的实际参数
  • 相关阅读:
    第2章 面试流程
    py面试指导系列-第1章 py服务端工程师面试指导
    十月的第一天
    I shall return
    k8S
    Eclipse编辑器基本设置
    Windows 下有什么软件能够极大地提高工作效率
    Bootstrap 轻量级后台管理系统模板--ACE使用介绍
    2014年将会受欢迎的IT技能--你有多少哪?
    面向程序员的数据库访问性能优化法则
  • 原文地址:https://www.cnblogs.com/atom-wangzh/p/8709650.html
Copyright © 2011-2022 走看看