反射
在运行期间,对于任意一个类,都能在运行时获取它的属性和方法,对于任意一个对象,都能调用它的任意属性和方法,这种动态获取信息和动态调用对象方法的功能就是反射机制。
获取到的属性和方法。将以普通对象的方式存在。与我们自己写的类并无任何区别。
大白话就是
当需要使用一个类时,我们都会先去实例化这个类,然后再用实例化的对象去操作,这是正射。正射的使用前提是我们得知道要实例化的对象。
而反射就是我们一开始不知道要初始化的对象(比如我们要去实例化老师、学生和学校,每次想实例化的可能不一样,那可以通过参数去选择要使用的实体,而这是在运行时动态传入的),只有在程序运行时才动态地去获取类信息和调用类的方法。
反射机制
类加载过程
- Java编译器编译.java文件,在磁盘中产生.class文件。.class文件是只能由JVM识别的机器码。
- JVM根据类加载器读取.class文件,取出二进制数据,加载到内存中解析。类加载器会根据类的全限定名去获取此类的二进制字节流;然后将该字节流代表的静态存储结构转化为方法区的运行时数据结构;然后,在内存中生成代表这个类的java.lang.Class对象。
- 加载结束后,JVM开始进行连接。经过这些操作后类的变量被初始化。
Class对象
Java 中,无论生成某个类的多少个对象,这些对象都会对应于同一个 Class 对象。这个 Class 对象是由 JVM 生成的,通过它能够获悉整个类的结构。
反射的本质就是:在运行时,把 Java 类中的各种成分映射成一个个的 Java 对象。
反射的应用场景
开发通用框架:
很多框架都是通过配置文件加载不同的对象或类,调用不同的方法,这就需要反射——运行时动态加载需要加载的对象。
动态代理:
在面向切面编程时(AOP),需要拦截特定的方法,需要反射来达成动态代理方式。
注解:
注解本身只是标记,需要利用反射机制去执行相应的行为。
可扩展性功能
可以通过使用完全限定名称创建可扩展性对象实例来使用外部的用户定义类。
使用反射
1、先获取Class对象
- 使用Class.forName()方法
Class c1 = Class.forName(“io.github.dunwu.javacore.reflect.ReflectClassDemo01”); - 直接获取对象的class
Class c1 = boolean.class; - 调用Object的getClass方法
Class c1 = bytes.getClass();
2、通过Field方法获取Class对象的成员
3、通过Method方法获取Class对象的方法
4、通过Constuctor方法获取Class对象的构造方法
静态代理
代理类在程序运行前就已经存在,代理类和委托类会实现同一接口或是派生自相同的父类。
可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类。
动态代理
代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据需要动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理由两组静态代理关系组成。在委托类和代理类中间加了一个中介类,中介类是原委托类构成一个静态代理关系,中介类是原委托类的代理类;中介类与原代理类也构成了一个静态代理关系,中介类是原代理类的委托类。这样就可以通过在中介类中写方法实现在原代理类方法的前后执行一些操作了,而不需要去原代理类所有方法前后都写相应的操作。