JAVA反射

JAVA反射

  • 什么是反射
  • 类加载
    • 过程
      • 1.加载
      • 2.连接
      • 3.初始化
  • Class对象
  • 反射的使用
    • 获取Class对象
    • 从class中获取信息
      • 获取对应的类的构造方法
      • 获取对应类包含的属性
      • 获取对应类包含的方法
      • 其他
  • 反射的应用


什么是反射

反射就是把Java类中的各种成分映射成一个个的Java对象

  • 一个java文件通过javac编译后得到class文件
  • 当我们执行初始化操作时(可能是new、父类初始化、子类初始化,反射等),会将class文件通过类加载器加载到JVM中
  • 加载的过程中,会在java堆中创建一个java.lang.Class对象,这个对象就代表这个类相关的信息
  • java中反射的原理就在于Class对象

JAVA反射_第1张图片


类加载

当程序主动使用某个类时,如果该类还未被加载到内存中,系统就会将类的class文件读入内存,并创建一个java.lang.Class对象,也就是当程序中使用任何类的时候,系统都会为之建立一个对应的Class对象

过程

系统会通过加载连接初始化三个步骤来给该类进行初始化,如果没有意外JVM将会连续完成这三个步骤,统称为类加载类初始化
JAVA反射_第2张图片

1.加载

调用ClassLoader的findClass方法找到.class文件,并把这个文件包含的字节码加载到内存中

2.连接

分为验证、准备和解析

  • 验证
    检查被加载的类是否有正确的内部结构并和其他类一致。包括文件格式验证、元数据验证、字节码验证、符合引用验证
  • 准备
    负责为类的静态属性分配内存和指定初始值,通常情况下设置默认初始值。这些变量所使用的的内存将在方法区进行分配
  • 解析
    该阶段是将常量池中的符号引用替换为直接引用的过程。主要针对类和接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符
     假设一个变量的定义如下:  
     public static int value = 123;
     变量value在准备阶段的值是0不是123,因为此时还未执行任何java方法
     
     假设一个变量的定义如下:  
     public static final  int value = 123;
     变量value在准备阶段的值是123,因为这是一个常量,存放在方法区的常量池中
    
    注意:解析过程不一定发生在初始化之前,可以发生在初始化之后。这是为了支持Java语言的运行时绑定(动态绑定)

3.初始化

类中静态属性和静态块的执行

  • 初始化时机
    虚拟机规范严格规定有且只有5种情况必须立即对类进行“初始化”:

    • 遇到newgetstaticputstaticinvokestatic这4条指令时。即使用new操作符创建实例、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)时、调用一个类的静态方法时
    • 使用java.lang.reflect包的方法对类进行反射调用时,如果没有进行初始化,则需要先出发触发其初始化。如:Class.forName("SuperClass")
    • 当初始化一个子类时,如果发现其父类没有初始化则需要先触发父类初始化
    • 当虚拟机启动,用户需要制定一个执行的主类(包含main()方法的类),虚拟机会先初始化执行的主类
    • 当使用JDK1.7的动态语言支持时,如果一个MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,且这个方法句柄对应的类没有初始化,则需要先触发其初始化

    除此之外所有引用类的地方都不会触发类初始化,被称为被动应用

  • 注意

    • 当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化
    • 当使用Class的forName()静态方法才会导致强制初始化该类
    • 当某个静态属性由final修饰且它的值可以在编译时得到,则程序在其它地方使用该静态属性时,实际上并不会使用该静态属性,而是相当于使用常量,所以不会导致该类被初始化
    • 如果final类型的静态属性的值不能在编译时得到,而是必须等到运行时才能确定该属性的值,则如果通过该类来访问此静态属性可以认为是主动访问使用该类,会导致类被初始化

Class对象

  • Class类的实例表示正在运行的Java应用程序中的类和接口,即JVM中众多实例每个类都有一个Class对象(包括基本数据类型)
  • Class类没有公共构造方法,Class对象是在加载类时由Java虚拟机以及调用类加载器中的defineClass方法自动构造的。不需要手动处理创建,由JVM创建
  • 运行期间一个类只会产生一个Class对象

反射的使用

实现Java反射机制的类都位于java.lang.reflect包中:

  • Class类:代表一个类
  • Field类:代表类的成员变量(类的属性)
  • Method类:代表类的方法
  • Constructor类:代表类的构造方法
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法

获取Class对象

  • 三种方法
    • 调用某个对象的getClass()方法,该方法返回该对象所属类的Class对象
      String s1 = "abc";
      Class c1 = s1.getClass();
      System.out.println(c1.getName());
      
      Student stu1 = new Student(); //这一new产生一个Student对象和一个Class对象
      Class stuClass = stu1.getClass(); //获取Class对象
      System.out.println(stuClass.getName());
      
    • 使用Class类的forName()静态方法
      Class c2 = Class.forName("java.lang.String"); //必须是真实路径,包名.类名
      System.out.println(c2.getName());
      
    • 调用某个类的class属性来获取该类对应的Class对象
      Class<String> c3 = String.class;
      System.out.println(c3.getName());
      

从class中获取信息

JAVA反射_第3张图片

获取对应的类的构造方法

  • 所有公有的构造方法:public Constructor[] getConstructors()
  • 所有构造方法(包括私有、受保护、默认、公有):public Constructor[] getDeclaredConstructors()
  • 指定公有的构造方法:public Constructor getConstructor(Class... parameterTypes)
    参数:Class... parameterTypes : 形参的Class对象
  • 指定某个构造方法:public Constructor getDeclaredConstructor(Class... parameterTypes)

注意:此处公有的是指此Class对象所表示的类的指定的public构造器,与构造器的级别没有关系

  • 创建对象实例
    • 使用Class对象的newInstance()方法来创建实例,要求该Class对象有默认的构造方法
    • 先用Class对象获取指定Constructor对象,再调用Constructor对象的newInstance()方法来创建实例

获取对应类包含的属性

  • 指定公有属性:getFields(String name)
  • 所有公有属性:getFields()
  • 调用Field的方法获取/设置属性值
    • getxxx(Object o):获取obj对象该Field的属性值。此处的xxx对应8个基本类型,如果该属性的类型是引用类型则取消get后面的XXX
    • setxxx(Object obj, xxx val):将obj对象的该Field设置为val值

获取对应类包含的方法

  • 所有公有方法:getMethods(),包含父类的和Object类的
  • 指定公有方法:getMethod(String name, Class... parameterTypes)
    • 参数name : 方法名;Class ... : 形参的Class类型对象
    • 调用方法
      调用Method的Object invoke(Object obj, Object...args):obj是执行该方法的主调,后面的args是执行该方法是传入该方法的实参,最后返回一个对象即方法返回的结果

其他

  • 获取对应类包含的注释
  • 获取对应类包含的内部类
  • 获取对应类所继承的父类实现的接口
  • 判断该类是否为接口,枚举,注释类型等

反射的应用

  • JDBC
  • Tomcat 的Servlet创建
  • Json 和Java对象互换
  • MyBatis 的OR/M (Object-Relation Mapping)
  • Spring Framework 的Bean容器
  • 生成JDK动态代理
    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理

参考:https://blog.csdn.net/sinat_38259539/article/details/71799078

你可能感兴趣的:(JAVA,java)