1 Class对象
Java的反射机制离不开一个被称为Class对象的特殊对象,它包含了与类有关的信息。
为什么说离不开Class对象呢?
因为Java反射机制就是:
在运行状态中,对任意一个实体类,都能够知道这个类的所有属性和方法;对任意一个对象,都能够调用它的任一方法和属性。
所以我们就先来弄清楚Class对象是个怎样“特殊”的对象,再去理解反射就容易多了。
2 Class类
类与对象(类实例)的关系:
对象是类的实例,类是对象的模板。
既然是Class对象,自然就又一个叫做Class类的实体类(虽然名字有点奇怪),我们可以在Java.lang包的Class.class文件中找到Class类的源码,有兴趣的同学,可以自己打开看看,这里贴上部分源码:
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
private static final int ANNOTATION= 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
private static native void registerNatives();
static {
registerNatives();
}
/*
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}
......
我们顺带的来简单的分析一下Class实体类:
- final修饰的类:Class类不可被继承的;
- Class:Class类是泛型类;
- 实现了四个接口;
- 没有公共的构造方法,源码注释说私有方法只有JVM能够调用并创建对象。
源码后面还有很多的方法,我们后面可以看到作用以及如何使用。
了解Class类了,我们下一步就是去探究Class对象如何创建了,我们需要的是Class对象!
3 生成Class对象
Class对象包含了与类有关的信息,类是程序的一部分,每个类都有一个Class对象,换言之,每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的地说,是被保存在一个同名的.class文件中)。为了生成这个类的对象,运行这个程序的JVM将使用被称为“类加载器”的子系统。
所有的类都是在对其第一次使用时,动态加载到JVM中的。下图描述的就是从类第一次使用,到生成Class对象的过程:
【1】程序运行时,类第一次使用,触发JVM进行类加载
常见的触发场景:
- 使用new关键字实例化对象
- 读取或设置一个类的静态字段(编译期常量除外)
- 调用一个类的静态方法
【2】通过一个类的全限定名来获取定义此类的二进制字节流
假设程序运行在本地,那么就是通过包名.类名到本地磁盘查找对应的.class文件,并将其加载到JVM中。
除了在本地.class格式文件获取以外,Class文件还有可能是从ZIP包、网络、数据库等其他形式的载体中读取的。当然这通常需要我们自定义类加载器,即重写类加载器的loadClass()方法。
【3】在内存中生成一个代表这个类的java.lang.Class对象
一旦Class对象被载入内存之后,它就被用来创建这个类的所有对象。
一个类在内存中有且仅有一个与之对应的Class对象,与该类产生的实例个数无关。
我们这里只是讲到Class对象的生成,并非是类加载的全过程,所以暂时不再展开后面的部分。
类加载的过程有三个步骤:
-
加载,这是由类加载器执行的。查找字节码,并从这些字节码中创建一个Class对象。
-
链接。验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
-
初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
Class对象的生成是在类的加载阶段。