反射概述
- 反射机制
- 平时我们使用某个类时,必定知道它是什么类,是用来做什么。
于是我们直接对这个类进行实例化,然后对类对象进行各种操作。
- 反射则是在运行时才知道要操作的类是什么,并且可以在运行时构造类,分析类的能力,获取类的属性并调用类的方法。
- 反射机制可以用来编写能够动态操纵Java代码的程序,满足动态查询类能力的开发需求。
反射的应用
- 反射是框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力
- 我们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。但是,这并不代表反射没有用。相反,正是因为反射,我们才能这么轻松地使用各种框架。
- 像Spring、Spring Boot、MyBatis等等框架中都大量使用了反射机制。这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。
- Java中的一大利器注解的实现也用到了反射。
- 为什么使用Spring的时候 ,一个@Component注解就声明了一个类为Spring Bean呢?为什么通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?
- 这些都是因为我们可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。
- 我们获取到注解之后,就可以做进一步的处理。
反射的优点与缺点
- 优点
- 可以让代码更加灵活
- 为各种框架提供开箱即用的功能提供了便利
- 缺点
- 让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。
- 反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。
- 优点
Class类
- Class类定义
- Class是一个特殊的类,用于保存对象运行时类型信息
- Class类位于java.lang包
- Class对象实际上表示的是一个类型,这个类型可能是引用型类型,也可能是基本类型
- JVM为每个类型管理一个唯一的Class对象,可以使用==运算符比较两个Class对象是否相同
这里Class对象的比较与instanceof运算符不同,比较结果与类的继承关系无关
Father father = new Father();
Son son = new Son();
//out: true
System.out.println("son instanceof Father: " + (son instanceof Father));
//out: true
System.out.println("son.getClass() == Son.class: " + (son.getClass() == Son.class));
//下述语句将报错,提示不可比较
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**
- **Class类的静态方法**`**forName()**`**可以根据类的全限定名获取对应的Class对象**
- 如果类名保存在一个字符串中,并且这个字符串可能会在运行时发生变化,即使用的类可能在运行时发生变化,就可以使用该方法
- 获取类的全限定名
可以使用Class类的实例方法getName()来获取类的全限定名
Employee e = new Employee();
Class c1 = e.getClass();
Class c2 = Class.forName("person.cyc.Employee");
Class c3 = int.class;
Class c4 = Employee[].class;
System.out.println("c1: " + c1.getName());
System.out.println("c2: " + c2.getName());
System.out.println("c3: " + c3.getName());
System.out.println("c4: " + c4.getName());
上述代码输出
c1: person.cyc.Employee
c2: person.cyc.Employee
c3: int
c4: [Lperson.cyc.Employee;
- 利用Class实例获取Class实例表示的类型的实例
- 这种方式是反射机制的精髓
- 可以利用Class实例获取其表示的类型的构造器类型(Constructor)实例,然后使用Constructor的实例方法
**newInstance()**
即可构造一个类的实例 - 实例
利用Class对象获取无参数的构造器并构造一个类的对象
Class cls = Class.forName("person.cyc.Employee");
Object obj = cls.getConstructor().newInstance();
//out: person.cyc.Employee
System.out.println(obj.getClass().getName());