最近在接触 Android 的组件化原理的时候,发现本身对 Java 反射的机制理解并非很深,只能利用周末的时间把 Java 反射机制回炉重铸。java
反射机制是 Java 语言提供的一种基础功能,赋予了 Java 程序在运行时的自省(introspect,官方用语)的能力。经过 Java 的反射机制,程序员能够在 Java 程序在运行态的时候操做任意的类或者对象的属性、方法。利用 Java 的反射机制,能够作到如下:程序员
对文中的"自省"的理解:"自省"应该仅指程序在运行时对自身信息(元数据)的检测,而反射机制不单单须要在运行时对程序的自身数据进行检测,还须要根据检测到的数据修改程序的状态或者方法。数组
用于操做反射的相关的 5 个类:ide
Constructor、Field、Method 这三个类都继承 AccessibleObject,该对象有一个很是重要的方法 AccessibleObject#setAccessible(boolean flag)
,这里的所谓 accessible 能够理解成修饰成员的 public、protected、private,这意味着咱们能够在程序运行时修改类成员的访问限制。函数
在类 Object 中有 Object#getClass()
、Object#hashCode()
、Object#equals(Object obj)
、Object#clone()
、Object#toString()
、Obect#notify()
、Object#notifyAll()
、Object#wait()
等 public 权限的方法。而 Object#getClass()
方法则是返回程序运行时的 Class 类的对象实例。CLass 类也是一样继承 Object 类,拥有相应的方法。Class 类的类表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,一个注解是一个接口。每一个数组还属于一个反映为Class对象的类,该对象由具备相同元素类型和维数的全部数组共享。Java 的基本类型 boolean、byte、char、short、int、long、float、double,和关键字 void 也表示为 Class 对象。组件化
Class 没有 public 类型的构造器。Java 虚拟机会在加载类时以及经过在类加载器中调用 ClassLoader#defineClass()
方法来自动构造 Class 对象。3d
Modifier 类提供了 static 方法和常量来解码类和成员访问修饰符。修饰符集合被表示为具备不一样修饰符的不一样位置的整数。表示的范围有 abstract、final、interface、native、private、protected、public、strict、synchronized、transient、volatile。code
java.lang.reflect.Constructororm
Constructor 提供了一个类的单个构造函数的信息和访问。Constructor 容许在将实际参数与 newINstance() 与底层构造函数的形式参数进行匹配时进行扩辗转换,若是发生缩小转换,则抛出 IllegalArgumentException
。cdn
方法 | 含义 |
---|---|
Constructor<?>[] getConstructors() | 返回包含一个 Constructor 对象的数组,元素表示为所指定的类的全部的 public 权限的构造函数 |
getDeclaredConstructors() | 表示返回包含 Constructor 对象的数组,元素表示为指定类的构造函数,包含非 public 权限的 |
getConstructor(class<?>... parameterTypes) | 返回一个 Constructor 对象,表示指定参数的类的 public 权限的构造函数 |
getDeclaredConstructor(class<?>... parameterTypes) | 表示返回一个表示 Constructor 对象,表示指定参数的构造函数,包含非 public 权限的 |
java.lang.reflect.Field
Field 提供有关类或接口的单个字段的信息和动态访问。 反射的字段能够是类(静态)字段或实例字段。Field 容许在获取或设置访问操做期间扩辗转换,但若是发生缩小转换,则抛出 IllegalArgumentException
。
方法 | 含义 |
---|---|
getFields() | 返回包含一个数组 Field 对象,表示的类或接口的全部可访问的公共字段 |
getDeclaredFields() | 返回包含一个数组 Field 对象,表示的类或接口声明的全部字段 |
getField(String name) | 返回包含一个数组 Field 对象,表示的类或接口指定的可访问的公共字段 |
getDeclaredField(String name) | 返回包含一个数组 Field 对象,表示的类或接口指定的任意权限的公共字段 |
java.lang.reflect.Method
Method 提供有关类和接口上单一方法的信息和访问权限。 反映的方法能够是类方法或实例方法(包括抽象方法)。
方法 | 含义 |
---|---|
getMethods() | 获取类全部的 public 方法 |
getMethod(String name, class<?>... parameterTypes) | 获取类特定的 public 方法 |
getDeclaredMethods() | 获取类全部的方法 |
getDeclaredMethod(String name, class<?>... parameterTypes) | 获取类特定的方法 |
Java 虚拟机能够经过称为运行时类型信息(RTTI, Run Time Type Information)的技术在运行时检查任何类,这是经过一种称为 Class 对象的特殊对象完成的,该对象包含有关类的信息。
虚拟机为每一个类管理一个独一无二的 Class 对象。也就是说,每一个类都有一个 Class 对象实例。在运行程序的时候,JVM 首先须要会去检测所需加载的类的 Class 是否已经完成加载。若是没有加载在 JVM 中,那么 JVM 回去寻找对应类名的 .class 文件,完成对 Class 对象的加载。经过 Class 对象,咱们能够实例化对应的 Class 类对象,调用其构造器(Constructor)、调用类的成员方法(Method)、访问或者修改类的成员属性(Field)。经过 AccessibleObject#setAccessible(boolean flag)
能够访问到类的非 public 权限的其余成员,在上文提到经过 AccessibleObject#setAccessible(boolean flag)
能够在程序运行时修改类成员的访问限制。实际上,AccessibleObject#setAccessible(boolean flag)
关闭了权限的访问检查,使得经过 Class#invoke()
能够访问到任意权限的类成员。
Java 的内部类可分为普通内部类、静态内部类。JVM 在编译含有普通内部类的时候,默认会在构造方法中传入外部类对象的引用,这也是为何内部类对象会持有外部类的引用。咱们能够经过解析 .class 字节码来验证这一推论。
有上面的基础,咱们能够推断出,在反射调用普通内部类的成员的时候,咱们须要在普通内部类的构造方法中传入外部类的对象引用。而静态内部类因为不持有外部类的引用,于是不须要在其构造方法中传入外部类的引用。
举个例子。有一个类 OutClass
,而后 OutClass
含有一个普通内部类 InnerClass
、以及静态内部类 StaticClass
。咱们经过反射分别实例化它们的时候,以下图:
反射调用普通内部类和静态内部类,只是在实例化的构造器的时候有区别,对于调用内部类的 Field、Method、Constructor,其过程是和调用普通类的过程时同样的,在这里就不一一细述了(主要由于我懒)。
Java 的反射机制用起来挺复杂的。但 Java 的反射机制在 Android 组件化中解耦合起到了很大的做用。能够在程序运行时访问类的成员属性或修改属性、执行方法、以及执行构造方法。而且在 Android 的许多源码中,有不少的属性、方法被标记了 @hide,但经过 Java 的反射,仍然能够访问这些属性、方法。