什么是反射机制
在程序运行时,动态的获取指定类的信息或动态调用类的方法的功能称为Java反射机制。Java反射机制可以在运行时获取指定类的所有属性和方法,能够调用它的任意一个方法。.
反射的原理
Java程序编译完之后每个类都会有一个对应的.class文件,里面保存了类的结构信息。而反射就是将指定类的.class文件加载到JVM中,生成一个唯一的不可改变的Class对象,通过Class对象就可以获取到类的信息。在Java中每个对象都有且只有一个Class对象。
为什么使用反射机制
反射的本质是动态的获取类的信息或动态操作类的方法,可以增加程序的灵活性。动态代理和框架设计就是基于反射机制。
反射的应用
Java多态的体现
Java动态代理
框架设计,如spring、hibernate、struts等。
反射的优缺点
优点:
- 可以动态获取类的信息、动态操作类的方法以及动态创建或修改类,提高了编程的灵活性。
- 降低耦合。
缺点:
- 性能低,使用反射时需要解析字节码等操作。
- 相对不安全,破坏了类的封装性(反射可以获取私有方法或属性)。
反射常用的类
- Class类:保存了类的所有结构信息,是反射机制的基础。
- Constructor类:提供了类的构造方法的操作,通过Class类获取。
- Field类:提供了类的字段信息、动态访问权限及操作,通过Class对象获取。
- Method类:提供了类的方法的信息和操作,通过Class对象获取。
Class类
在Java中对象分为实例对象和Class对象(运行时类型)。实例对象就是通过new创建的对象,其类型在编译器就已经确定(RRTI)。Class对象的类型和信息是在运行时类型识别的(RTTI),允许在程序运行时动态的创建类以及编写类的信息。其实实例对象也是通过Class对象创建,当我们new创建一个对象时,JVM会先查找出对应的Class对象,然后调用构造器方法创建实例对象。
基本类型 (boolean, byte, char, short, int, long, float, and double)有Class对象,数组有Class对象,关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。
- Class本身也是一个类。
- Class对象只能由JVM创建。
- 一个类有且只有一个Class对象。
- 一个Class对象对应着一个加载到JVM中的.class文件。
- Class对象保存了一个类的所有结构信息是反射的基础,任何动态加载、动态创建以及动态修改类的必须先获取到Class对象。
Class的加载过程
Class类没有公共构造方法,是在由JVM通过类加载器构造的,加载过程大致分为三个阶段:
- 加载,通过类的全限定类名找到对应的.class文件,获取其二进制字节流(class字节码)将其静态存储结构转换成方法区的运行时数据接口,根据class字节码在Java堆中生成一个Class对象。
- 链接,链接右细分了三个小阶段:
- 验证,验证字节码的安全性和完整性。
- 准备,为静态域分配存储空间(不包含实例成员)。
- 解析,解析这个类对其他类的所有引用。
- 初始化,到了此阶段,才真正开始执行类中定义的java程序代码。用于执行该类的静态初始器和静态初始块,如果该类有父类的话,则优先对其父类进行初始化。
Class对象获取方式
- Class.forName("全限定类名"):forName方法来自于Class类,使用这种方法无需通过类的实例对象或者通过类名就可以获取到指定类的Class对象,耦合性低。
- 实例对象.getClass():getClass方法来自与Object类,依赖于类的实例对象。
- 类名.class:class是类字面常量。相较于前两种方法效率更高和更安全。因为通过类字面常量只会触发类加载阶段的加载阶段,而其他两种会触发全部阶段;通过这种方式编译器会在编译器对类型进行检查。使用类字面常量不仅可以应用于普通的类,也可以应用用接口,数组以及基本数据类型。
基本数据类型还有对应的基本包装类型,其包装类型有一个标准字段TYPE,而这个TYPE就是一个引用,指向基本数据类型的Class对象,其等价转换如下
boolean.class = Boolean.TYPE;
char.class = Character.TYPE;
byte.class = Byte.TYPE;
short.class = Short.TYPE;
int.class = Integer.TYPE;
long.class = Long.TYPE;
float.class = Float.TYPE;
double.class = Double.TYPE;
void.class = Void.TYPE;