Java的反射机制
什么是反射?
Java的反射是指程序在运行期间可以拿到一个对象的所有信息。
正常情况下,如果我们要调用一个对象的方法,或者访问一个对象的字段,通常会传入对象的实例。
import cn.wustxiao.learnjava.Person;
public class Main{
String getFullName(Person p){
return p.getFirstName() + " " + p.getLastName();
}
}
但是如果不能获得Person
类,只有一个Object
实例,比如这样:
String getFullName(Object obj){
return ???
}
所以,反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。
Class类
除了int
等基本类型外,Java的其他类型全部都是class
(包括interface
)。例如:
String
Object
Runnable
Exception
仔细思考,我们可以得出结论: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
类型是一个名叫Class
的class
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
实例都指向一个数据类型(class
或interface
)
一个Class实例包含了该class的所有完整信息:
Class Instance -- String
name = "java.lang.String"
package = "java.lang"
super = "java.lang.Object"
interface = CharSequence...
field = value[],hash,...
method = indexOf()...
如何通过Class
实例获取到该实例对应的class
的所有方法
由于JVM为每个加载的class
创建了对应的Class
实例,并在实例中保存了该Class
的所有信息,包括类名,包名,父类,实现的接口,所有方法,字段等,因此,如果获取了某个Clsss
实例,我们就可以通过这个Class
实例获取到该实例对应的class
的所有信息
这种通过Class
实例获取class
信息的方法称为反射
如何获取一个class
的Class
实例?有三个方法:
方法一:直接通过一个class
的静态变量class
获取:
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()
方法获取:
String s = "Hello"
Class cls = s.getClass();
方法三:如果知道一个class
的完整类名,可以通过静态方法Class.forName()
获取:
Class cls = Class.forName("java.lang.String");
因为Class
实例在JVM中是唯一的,所以,上述方法获取的Class
实例是同一个实例。可以用==
比较两个Class
实例:
Class cls1 = String.class;
String s = "Hello";
Class cls2 = s.getClass();
Class cls3 = Class.forName('java.lang.String');
boolean sameClass = cls1 = cls2 = cls3; // true
注意一下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
动态加载class
的特性对于Java
程序非常重要。利用JVM动态加载class
的特性,我们才能在运行期根据条件加载不同的实现类。