反射是一种计算机处理方式,是程序可以访问、检测和修改它本身状态或行为的一种能力。Java 反射使得我们可以在程序运行时动态加载一个类,动态获取类的属性、方法、构造函数等信息。反射使 Java 这一静态语言有了动态的特性。
使用
Example
public class Apple { |
API
类的实例化和构造函数
获取公有构造函数,不包括父类,Class.class
public Constructor>[] getConstructors()
public ConstructorgetConstructor(Class>… parameterTypes) 获取当前类构造函数,忽略修饰符
public Constructor>[] getDeclaredConstructors()
public ConstructorgetDeclaredConstructor(Class>… parameterTypes)
构造函数调用,Constructor.class
public T newInstance(Object… initargs)忽略修饰符,强制调用
public void setAccessible(boolean flag)
类成员变量的获取
获取公有变量,包括父类,Class.class
public Field[] getFields()
public Field getField(String name)获取当前类成员变量,忽略修饰符
public Field[] getDeclaredFields()
public Field getDeclaredField(String name)
成员变量赋值,Field.class
//obj为实例对象
public void set(Object obj,Object value)忽略修饰符,强制调用
public void setAccessible(boolean flag)
类方法的获取
获取公有方法,包括父类,Class.class
public Method[] getMethods()
public Method getMethod(String name, Class>… parameterTypes)获取当前类方法,忽略修饰符
public Method[] getDeclaredMethods()
public Method getDeclaredMethod(String name, Class>… parameterTypes)
方法调用,Method.class
//obj为类实例化对象,如果为静态方法obj为Null
invoke(Object obj, Object… args)忽略修饰符,强制调用
public void setAccessible(boolean flag)
类注解的获取
获取类的”annotationClass”类型的注解,包括父类
public Annotation getAnnotation(Class annotationClass)// 获取类的全部注解 ,包括父类
public Annotation[] getAnnotations()// 获取类自身声明的全部注解 ,忽略修饰符
public Annotation[] getDeclaredAnnotations()
类父类的获取
获取实现的全部接口
public Type[] getGenericInterfaces()获取父类
public Type getGenericSuperclass()
原理
RTTI和Class对象
RTTI,即 Run-Time Type Identification,运行时类型识别。RTTI 能在运行时就能够自动识别每个编译时已知的类型。
很多时候需要进行向上转型,比如 Fruit 类派生出 Apple 类,当现有的方法将 Fruit 作为参数时,如果我们传入其派生类的引用,那么 RTTI 就会起作用。通过 RTTI 识别出 Apple 类是 Fruit 的派生类,然后向上转型。特别是在用接口类型作为参数的时候,这一特性更是被频繁使用。
而这些类型信息是通过一个特殊对象Class(java.lang.Class)实现的,它包含跟类相关的信息。
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。
- 加载 :将 class 文件读入内存,并为之创建一个 Class 对象。任何类被使用时系统都会建立一个 Class 对象。
连接:
- 验证 是否有正确的内部结构,并和其他类协调一致
- 准备 负责为类的静态成员分配内存,并设置默认初始化值
- 解析 将类的二进制数据中的符号引用替换为直接引用
初始化:如果该类有超类,则对其初始化,执行静态域和静态初始化块。
获取 Class 对象的方式有三种
Object 类的 getClass() 方法,执行静态块和动态构造块
数据类型的静态属性 class ,不会初始化该类
Class类中的静态方法
public static Class forName(String className)
,执行静态块,不执行动态构造块
RTTI和反射
Java 有两种 RTTI 方式,一种是传统的,假设在编译时已经知道了所有的类型;还有一种,是利用反射机制,在运行时再尝试确定类型信息。
RTTI和反射之间的真正区别只在于:
RTTI:编译器在编译时打开和检查.class文件
反射:运行时打开和检查.class文件
严格的说,反射也是一种形式的RTTI,不过,一般的文档资料中把RTTI和反射分开,因为一般的,大家认为 RTTI指的是传统的 RTTI,通过继承和多态来实现,在运行时通过调用超类的方法来实现具体的功能
未完待续