深刻理解Java反射机制

前言

最近在接触 Android 的组件化原理的时候,发现本身对 Java 反射的机制理解并非很深,只能利用周末的时间把 Java 反射机制回炉重铸。java

什么是Java反射

反射机制是 Java 语言提供的一种基础功能,赋予了 Java 程序在运行时的自省(introspect,官方用语)的能力。经过 Java 的反射机制,程序员能够在 Java 程序在运行态的时候操做任意的类或者对象的属性、方法。利用 Java 的反射机制,能够作到如下:程序员

  • 在程序的运行态能够获取对象所属的类;
  • 在程序的运行态能够构造类的对象实例;
  • 在程序的运行时能够获取,或者修改类的成员属性;
  • 在程序的运行态能够调用某个类,或者对象的方法;
  • 在程序的运行态能够获取类的其余信息,好比描述修饰符、父类信息等;

对文中的"自省"的理解:"自省"应该仅指程序在运行时对自身信息(元数据)的检测,而反射机制不单单须要在运行时对程序的自身数据进行检测,还须要根据检测到的数据修改程序的状态或者方法。数组

用于操做反射的相关的 5 个类:ide

  • java.lang.Class:表明类;
  • java.lang.reflect.Constructor:表明类的构造方法;
  • java.lang.reflect.Field:表明类的属性;
  • java.lang.reflect.Method:表明类的方法;
  • java.lang.reflect.Modifier:表明类、方法、属性的修饰符;

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

Constructor

java.lang.reflect.Constructororm

Constructor 提供了一个类的单个构造函数的信息和访问。Constructor 容许在将实际参数与 newINstance() 与底层构造函数的形式参数进行匹配时进行扩辗转换,若是发生缩小转换,则抛出 IllegalArgumentExceptioncdn

方法 含义
Constructor<?>[] getConstructors() 返回包含一个 Constructor 对象的数组,元素表示为所指定的类的全部的 public 权限的构造函数
getDeclaredConstructors() 表示返回包含 Constructor 对象的数组,元素表示为指定类的构造函数,包含非 public 权限的
getConstructor(class<?>... parameterTypes) 返回一个 Constructor 对象,表示指定参数的类的 public 权限的构造函数
getDeclaredConstructor(class<?>... parameterTypes) 表示返回一个表示 Constructor 对象,表示指定参数的构造函数,包含非 public 权限的

Field

java.lang.reflect.Field

Field 提供有关类或接口的单个字段的信息和动态访问。 反射的字段能够是类(静态)字段或实例字段。Field 容许在获取或设置访问操做期间扩辗转换,但若是发生缩小转换,则抛出 IllegalArgumentException

方法 含义
getFields() 返回包含一个数组 Field 对象,表示的类或接口的全部可访问的公共字段
getDeclaredFields() 返回包含一个数组 Field 对象,表示的类或接口声明的全部字段
getField(String name) 返回包含一个数组 Field 对象,表示的类或接口指定的可访问的公共字段
getDeclaredField(String name) 返回包含一个数组 Field 对象,表示的类或接口指定的任意权限的公共字段

Method

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反射实践

Java反射调用内部类

Java 的内部类可分为普通内部类、静态内部类。JVM 在编译含有普通内部类的时候,默认会在构造方法中传入外部类对象的引用,这也是为何内部类对象会持有外部类的引用。咱们能够经过解析 .class 字节码来验证这一推论。

有上面的基础,咱们能够推断出,在反射调用普通内部类的成员的时候,咱们须要在普通内部类的构造方法中传入外部类的对象引用。而静态内部类因为不持有外部类的引用,于是不须要在其构造方法中传入外部类的引用。

举个例子。有一个类 OutClass,而后 OutClass 含有一个普通内部类 InnerClass、以及静态内部类 StaticClass。咱们经过反射分别实例化它们的时候,以下图:

反射调用普通内部类和静态内部类,只是在实例化的构造器的时候有区别,对于调用内部类的 Field、Method、Constructor,其过程是和调用普通类的过程时同样的,在这里就不一一细述了(主要由于我懒)。

小结

Java 的反射机制用起来挺复杂的。但 Java 的反射机制在 Android 组件化中解耦合起到了很大的做用。能够在程序运行时访问类的成员属性或修改属性、执行方法、以及执行构造方法。而且在 Android 的许多源码中,有不少的属性、方法被标记了 @hide,但经过 Java 的反射,仍然能够访问这些属性、方法。

相关文章
相关标签/搜索