如下图所示是ClassLoader加载一个class文件到JVM时需要经过的步骤。
第一个阶段是找到.class文件并把这个文件包含的字节码加载到内存中。
第二个阶段又可以分为三个步骤,分别实字节码验证、Class类数据结构及相应的内存分配和最后的符号表的链接。
第三个阶段是类中静态属性和初始化赋值,以及静态块的执行等。
加载字节码到内存
其实在抽象类ClassLoader中没有定义如何去加载,如何去找到指定类并且把他的字节码加载到内存需要的子类中去实现,也就是要实现findClass()方法。我们看一下子类URLClassLoader是如何实现findClass的,在URLClassLoader中通过一个URLClassPath类帮助取得要加载的class文件字节流,而这个URLClassPath定义了到哪里去找这个class文件,如果找到了这个class文件,在读取他的byte字节流,通过调用defineClass()方法来创建类对象。
这个实现机制如同inputStream和OutputStream一样,只是定义了读取文件的机制和形式,并没有定义从哪里和如何读取他。
我们再看看URLClassLoader类的构造函数,可以发现必须要指定一个URL数据才能够创建URLClassLoader对象,也就是必须要指定这个ClassLoader默认到哪个目录下去查找class文件。
这个URL数组也是创建URLClassPath对象的必要条件。从URLClassPath的名字中就可以发现他是通过URL的形式来表示ClassPath路径的。
在创建URLClassPath对象时会根据传过来的URL数组中的路径来判断是文件还是jar包,根据路径的不同分别创建FileLoader或者JarLoader,或者使用默认的加载器。当JVM调用findClass时由这几个加载器来将class文件的字节码加载到内存中。
如何设置每个ClassLoader的搜索路径呢?下表所示是Bootstrap ClassLoader、ExtClassLoader和AppClassLoader的参数形式。
ClassLoader类型 | 参数选项 | 说明 |
---|---|---|
Bootstrap ClassLoader | -Xbootclasspath: | 设置Bootstrap ClassLoader的搜索路径 |
-Xbootclasspath/a: -Xbootclasspath/p: | 把路径添加到已存在Bootstrap ClassLoader搜索路径的后面 把路径添加到已存在Bootstrap ClassLoader搜索路径的前面 | |
ExtClassLoader | -Djava.ext.dirs | 设置ExtClassLoader的搜索路径 |
AppClassLoader | -Djava.class.path=-cp 或-classpath | 设置AppClassLoader的搜索路径 |
在上面的参数设置中,最常用到的就是设置classpath的环境变量,因为通常都是让Java运行指定的程序。如果在通过命令行执行一个类时出现NoClassDefFoundError错误,那么很可能是没有指定classpath所致,或者指定了classpath但是没有指明包名。
验证与解析
- 字节码验证,类装入器对于类的字节码要做许多检测,以确保格式正确、行为正确。
- 类准备,在这个阶段准备代表每个类中定义的字段、方法和实现接口所必须的数据结构。
- 解析,在这个阶段类装入器装入类所引用的其他所有类。可以用许多方式引用类,如超类、接口、字段、方法签名、方法中使用的本地变量。
初始化Class对象
在类中包含的静态初始化器都被执行,在这一阶段末尾静态字段被初始化为默认值。