• 15、反射机制


    反射机制:

     

      1、反射就是把Java类中的各种成分映射成相应的java类。

     

         例如:一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等是一个个的类,表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

     

     

      2、一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象来使用。

     

     

      Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性

    这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

        能动态获取类中信息,就是java的反射机制,可以理解为对类的解剖

      如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,怎么可以实现呢?

        这时就要使用到了反射的技术。

        反射技术大大提高了程序的扩展性

     

    Tomcat

      提供了处理请求和应答的方式,因为具体的处理动作不同,所以对外提供了接口,有开发者来实现具体的请求和应答处理

      接口为:Servlet

        反射一般有接口和配置文件

     

    学习框架,一定要明确:

      1、这个框架是干嘛的

      2、这个框架的配置文件

     

    Class类型

      Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于直观属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,属于同一类事物,用一个类来描述这类事物,这个类就是Class,要注意与小写class关键字的区别。Class类描述了类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表等等。

        1、Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。

     

      2、对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?

        *人-->Person

        *Java类-->Class

     

      3、对比提问:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?

        *Class类对应各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码等等;

          字节码文件:java源程序编译后生成的二进制数据文件,当需要使用该类描述事物时,必须先将该类字节码文件加载进内存,才可以用该字节码来创建对象。每一个字节码就是一个Class类实例对象。

        *一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?该类型即为Class类型;

     

      4、获得各个字节码对应的实例对象(Class类型)

        *类名.class,例如,System.class

        *对象.getClass(),例如,new Date().getClass()

          有了对象,就可以用对象调用getClass()方法来获取其自身的字节文件。

        *Class.forName("类名"),例如,Class.forName("java.util.Date");

          Class的静态方法forName("类名(完整类名,包括了包名)")可以得到这个类的字节码文件。

          得到字节码文件有两个情况:

          **该字节码已经加载进内存了,则不需再加载,直接找到该字节码返回即可

          **虚拟机里还没有该字节码,则用类加载器去加载,加载进以后先缓存起来,再将刚加载进了字节码文件返回。

    在反射里比较常用第三种方式,因为写源程序时还不知道类的名字,只是在运行时才接受传入包含类名的字符串(通过配置文件),则在写源程序时,将类名用字符串类型的变量表示,用于接收类名。

    代码示例:

    String str1 = "abc";
    
    
    Class cls1 = str1.getClass();
    
    
    Class cls2 = String.class;
    
    
    Class cls3 = Class.forName("java.lang.String");
    
    
    System.out.println(cls1==cls2);
    
    
    System.out.println(cls2==cls3);

    结果是true,故三种方式获取的字节码文件是同一个。

     

      5、九个预定义Class实例对象:

        只要是一个类型,就对应着一个Class实例对象

        *八个基本数据类型(boolean,byte,char,short,int,long,float,doublie)都有有其对应的Class实例对象

        *void也有对应的Class实例对象。

     

      6、数组类型的Class实例对象

        Class isArray()

        只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void...

     

    Class类

      该类可以获取字节码文件中的所有内容,包括名称,字段,构造函数,一般函数等

      反射就是依靠Class类完成的

      想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。

     

    Class类的实例表示正在运行的Java应用程序中的类和接口。

     

      要想对字节码文件进行解剖,必须要有字节码文件对象

      如何获取字节码文件对象呢?

        获取字节码文件对象的方式:

          1、Object类中的getClass()方法

            想要用这种方式,必须要明确具体的类,并创建对象

     

          2、任何数据类型都具备一个静态的属性.class来获取其对应的Class对象

            相对简单,但是还是要明确用到类中的静态成员,还是不够扩展

     

          3、只要通过给定的类的字符串名称,就可以获取该类,更为扩展

            可以用Class类中的forName(String className);返回与带有给定字符串名的类或接口相关联的Class对象。

    String className = "cn.itcast.bean.Person";
    
    Class<?> clazz = Class.forName(className);
    
    System.out.println(clazz);

      

    Class对象中方法

      newInstance();创建此Class对象所表示的类的一个新实例。如同用一个带有一个空参数列表的new表达式实例化该类。如果该类尚未初始化,则初始化这个类。

      Object obj = clazz.newInstance();

     

    获取构造函数

      当获取指定名称对应类中的所体现的对象时,而该对象初始化不使用空参数构造函数,怎么办呢?

      既然是通过指定的构造函数进行对象的初始化,所以应该先获取到该构造函数,通过字节码文件对象即可完成

      该方法是:getConstructor(Class<?>... parameterTypes)

      Class类中的getConstructor(Class<?>... parameterTypes)方法,会返回一个Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。

    Class<?> clazz = Class.forName(name);
    
    //获取指定的构造函数对象 
    
    Constructor<?> con = clazz.getConstructor(String.class,int.class);
    
    //通过该构造器对象的newInstance方法进行对象的初始化
    
    Object obj = con.newInstance("小强",39);

     

    Constructor<T>类

      Constructor提供关于类的单个构造方法的信息以及对它的访问权限。

    反射包:java.lang.reflect

    AccessibleObject类

      AccessibleObject类是Field、Method和Constructor对象的基类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。

     

    获取字段

      Class类中的方法getField(String name);返回一个Field对象,它反映此Class对所表示的类或接口的指定的公共成员字段。Name为成员名

      getFields()返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段。

      getDeclaredField(String name);返回一个Field对象,该对象反映此Class对象所表示的类或接口的指定已声明字段(即private声明的字段也可以获得);

      getDeclaredFields();返回Field对象的一个数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段。

      如果字段是私有的,访问该字段必须采用暴力访问,AccessibleObject类中方法setAccessible(boolean flag);将此对象的accessible标志设置为指示的布尔值。值为true则指示反射的对象在使用时应该取消Java语言访问检查。

    //只获得本类,还包含私有
    
    Field field = clazz.getDeclaredField("age");
    
    //对私有字段的访问取消权限检查,暴力访问。
    
    field.setAccessible(true);
    
    System.out.println(field.get(obj));

     

    获取方法

      Class类中方法:getMethod(String name,Class<?>... parameterTypes);返回一个Method对象,它反映此Class对象所表示的类或接口的指定公共成员方法。Name参数是一个String,用于指定所需方法的简称。parameterTypes参数是按声明顺序标识该方法形参类型的Class对象的一个数组。如果parameterTypeset为null,则按空数组处理

    Class clazz = Class.forName("cn.itcast.bean.Person");
    
    Method method = clazz.getMethod("show", null);//获取空参数一般方法
    
    System.out.println(method);
    
    Object obj = clazz.newInstance();
    
    method.invoke(obj, null);

      获取方法后,采用Method对象中的方法invoke(Object obj,Object... args);来调用

      Invoke(Object obj,Object... args)方法对带有指定参数的指定对象调用由此Method对象表示的底层方法。

     

      getMethods();返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口的)公共member方法。

    Method[] methods = clazz.getMethods();//获取的都是公有的方法

    for(Method method : methods){   System.out.println(method); }

       getDeclaredMethods();获取本类中的所有方法,包含私有的

      methods = clazz.getDeclaredMethods();//只获取被类中的所有方法,包含私有

     

       获取其中一个方法:getMethod(String name,Class<?>... parameterTypes);需要明确方法名和参数列表

     

    用反射方式执行某个类中的main方法 

    String startingClassName = args[0];
    
    Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
    
    mainMethod.invoke(null, newObject[]{new String[]{"111","222","333"}});//传入数组时,不会将其作为一个元素看待,而是会将其拆开,其元素作为参数传入,则此时就成了三个参数传入了,为了避免这种情况,有两种方法,上例为其中一种,即为其外在包装一个Object数组,将要传入的数组作为元素;第二种是将该数组类型提升为Object类型,作为一个元素传入。

     

    数组的反射 

      1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象;

    int[] a1 = new int[3];
    
    int[] a2 = new int[4];
    
    System.out.println(a1.getClass()==a2.getClass());//返回true

       2、代表数组的Class实例对象的getSuperclass()方法返回父类为Object类对应的Class。

    System.out.println(a1.getClass().getSuperclass().getName());

     

      3、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。

     

      4、Arrays.asList()方法处理int[]和String[]时的差异。

     

      5、Array工具类用于完成对数组的反射操作。

    private static void printObject(Object obj) {
    
      Class clazz = obj.getClass();
    
      if(clazz.isArray()){//判断是否为数组
    
        int len = Array.getLength(obj);//获取数组长度
    
        for(int i = 0;i<len;i++){//打印数组中的元素
    
          System.out.println(Array.get(obj, i));
    
        }
    
      }else{
    
        System.out.println(obj);
    
      }
    
    }

     

      HashCode作用:基于哈希表结构的集合才有用。用于计算出该元素的哈希值,方便分区域存储,下次存数据时可以针对性的找到对应位置看是否有相同元素。

      内存泄露现象:当对象不需要再用了,但该对象一直没被释放,这样就是内存泄露。

      

    反射的作用->实现框架功能

       1、框架与框架要解决的核心问题

          反射的作用,主要用来写框架。

          框架是先写好的程序,而其所有调用的对象类名还不知道,或许该类都还不存在,在这种情况下需要用到反射的方法,运行时从外部传入要调用的类名。

          解决调用还未存在的类创建对象的问题。

          例子:房子(框架)、门(自己编写的类)和锁(自己要调用的类)的故事

     

      2、应用反射原理实现框架调用外部程序

    // InputStream ips = new FileInputStream("config.properties");
    
    // InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
    
    InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties");
    
    Properties props = new Properties();
    
    props.load(ips);
    
    String className= props.getProperty("className");
    
    Collection collections = (Collection)Class.forName(className).newInstance();

        一定要自己用完整的路径,但完整的路径不是硬编码,而是运算出来的。

     

    内省->了解JavaBean

       1、JavaBean是一种特殊的Java类,主要用于传递信息,这种java类中的主要用于访问私有的字段,且方法名符合某种命名规则。

         IntroSpector-->JavaBean-->特殊的Java类

         JavaBean类是一种规则,其内必须包含get()和set()方法。

         Int getAge();

         void setAge();

     

      2、如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问。

          JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。

        去掉get和set前缀后,剩下的是JavaBean的属性名,用该属性名时要把首字母该为小写,前提是第二个字母也是小写的。

         例:gettime-->time

              getTime-->time

              getCPU-->CPU

     

       3、一个符合JavaBean特点的类可以当作普通类一样进行使用。把它当作JavaBean用的好处:

        *在Java EE开发中,经常要使用到JavaBean。

        *JDK中提供了对JavaBean进行操作的一些API,这套API称为内省。

     

  • 相关阅读:
    oracle 日期函数
    SharpDevelop学习笔记(5)—— AddIns系统详解
    C#3.0 为我们带来什么(2) —— 自动属性
    SharpDevelop学习笔记(6)—— AddIn构建指南
    SharpDevelp2.0学习笔记(1)——SharpDevelp简单介绍
    对象数组根据某属性列的灵活排序
    SharpDevelop学习笔记(4)——SharpDevelop的核心
    也谈2007
    SharpDevelop学习笔记(2)——体系结构
    C#3.0 为我们带来什么(1) —— LINQ之Lambda
  • 原文地址:https://www.cnblogs.com/zyh-blog/p/3259587.html
Copyright © 2020-2023  润新知