反射概述

  • 反射机制
    • 平时我们使用某个类时,必定知道它是什么类,是用来做什么。

于是我们直接对这个类进行实例化,然后对类对象进行各种操作。

  • 反射则是在运行时才知道要操作的类是什么,并且可以在运行时构造类,分析类的能力,获取类的属性并调用类的方法
  • 反射机制可以用来编写能够动态操纵Java代码的程序,满足动态查询类能力的开发需求。
  • 反射的应用

    • 反射是框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力
    • 我们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。但是,这并不代表反射没有用。相反,正是因为反射,我们才能这么轻松地使用各种框架。
    • 像Spring、Spring Boot、MyBatis等等框架中都大量使用了反射机制。这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射
    • Java中的一大利器注解的实现也用到了反射。
      • 为什么使用Spring的时候 ,一个@Component注解就声明了一个类为Spring Bean呢?为什么通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?
      • 这些都是因为我们可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。
      • 我们获取到注解之后,就可以做进一步的处理。
  • 反射的优点与缺点

    • 优点
      • 可以让代码更加灵活
      • 为各种框架提供开箱即用的功能提供了便利
    • 缺点
      • 让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。
      • 反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

Class类

  • Class类定义
    • Class是一个特殊的类,用于保存对象运行时类型信息
    • Class类位于java.lang包
    • Class对象实际上表示的是一个类型,这个类型可能是引用型类型,也可能是基本类型
    • JVM为每个类型管理一个唯一的Class对象,可以使用==运算符比较两个Class对象是否相同

这里Class对象的比较与instanceof运算符不同,比较结果与类的继承关系无关

  1. Father father = new Father();
  2. Son son = new Son();
  3. //out: true
  4. System.out.println("son instanceof Father: " + (son instanceof Father));
  5. //out: true
  6. System.out.println("son.getClass() == Son.class: " + (son.getClass() == Son.class));
  7. //下述语句将报错,提示不可比较
  8. System.out.println("son.getClass() == Father.class: " + (son.getClass() == Father.class));
  • 获取Class实例
    • 通过对象实例获取

**Class<?> object.getClass()**
Object类的实例方法getClass()返回保存该对象运行时类型信息的Class类实例

  • 知道具体类时,可以直接通过类的静态属性class获取对应的Class对象
  • 不知道具体类时,使用下面的方法遍历包下面的类来获取Class对象

**Class<?> Class.forName(String className) throws ClassNotFoundException**

  1. - **Class类的静态方法**`**forName()**`**可以根据类的全限定名获取对应的Class对象**
  2. - 如果类名保存在一个字符串中,并且这个字符串可能会在运行时发生变化,即使用的类可能在运行时发生变化,就可以使用该方法
  • 获取类的全限定名

可以使用Class类的实例方法getName()来获取类的全限定名

  1. Employee e = new Employee();
  2. Class c1 = e.getClass();
  3. Class c2 = Class.forName("person.cyc.Employee");
  4. Class c3 = int.class;
  5. Class c4 = Employee[].class;
  6. System.out.println("c1: " + c1.getName());
  7. System.out.println("c2: " + c2.getName());
  8. System.out.println("c3: " + c3.getName());
  9. System.out.println("c4: " + c4.getName());

上述代码输出

  1. c1: person.cyc.Employee
  2. c2: person.cyc.Employee
  3. c3: int
  4. c4: [Lperson.cyc.Employee;
  • 利用Class实例获取Class实例表示的类型的实例
    • 这种方式是反射机制的精髓
    • 可以利用Class实例获取其表示的类型的构造器类型(Constructor)实例,然后使用Constructor的实例方法**newInstance()**即可构造一个类的实例
    • 实例

利用Class对象获取无参数的构造器并构造一个类的对象

  1. Class cls = Class.forName("person.cyc.Employee");
  2. Object obj = cls.getConstructor().newInstance();
  3. //out: person.cyc.Employee
  4. System.out.println(obj.getClass().getName());