reflection 反射
Table of Contents

Reflection

反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。

反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法

原理

除了int等基本类型外,Java的其他类型全部都是class(包括interface)。例如:

可以得出结论:class(包括interface)的本质是数据类型(Type)。无继承关系的数据类型无法赋值:

Number n = new Double(123.456); // OK
String s = new Double(123.456); // compile error!

class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。

每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。注意:这里的Class类型是一个名叫Classclass。它长这样:

public final class Class {
    private Class() {}
}

String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:

Class cls = new Class(String);

这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。

所以,JVM持有的每个Class实例都指向一个数据类型(classinterface):

┌───────────────────────────┐
      Class Instance       │──────> String
├───────────────────────────┤
name = "java.lang.String"  
└───────────────────────────┘
┌───────────────────────────┐
      Class Instance       │──────> Random
├───────────────────────────┤
name = "java.util.Random"  
└───────────────────────────┘
┌───────────────────────────┐
      Class Instance       │──────> Runnable
├───────────────────────────┤
name = "java.lang.Runnable"
└───────────────────────────┘

一个Class实例包含了该class的所有完整信息:

┌───────────────────────────┐
      Class Instance       │──────> String
├───────────────────────────┤
name = "java.lang.String"  
├───────────────────────────┤
package = "java.lang"      
├───────────────────────────┤
super = "java.lang.Object" 
├───────────────────────────┤
interface = CharSequence...
├───────────────────────────┤
field = value[],hash,...   
├───────────────────────────┤
method = indexOf()...      
└───────────────────────────┘

由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。

这种通过Class实例获取class信息的方法称为反射(Reflection)。

获取实例

方法一:直接通过一个class的静态变量class获取:

Class cls = String.class;

方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

String s = "Hello";
Class cls = s.getClass();

方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:

Class cls = Class.forName("java.lang.String");

子类比较 instanceof

Class实例比较和instanceof的差别:

Integer n = new Integer(123);

boolean b3 = n instanceof Integer; // true
boolean b4 = n instanceof Number; // true

boolean b1 = n.getClass() == Integer.class; // true
boolean b2 = n.getClass() == Number.class; // false

instanceof不但匹配当前类型,还匹配当前类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。

通常情况下,我们应该用instanceof判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型。只有在需要精确判断一个类型是不是某个class的时候,我们才使用==判断class实例