Java是介于编译型和解释型之间的编程语言,编译器将java源代码编译成中间层字节码文件(bytecode),由Java虚拟机(Java Virtual Machine)解释和执行之。Java体系结构可以表示如下:
由上图可知类加载器是JVM的一部分,主要作用是将字节码加载进入执行引擎,以供执行。当使用java A 的时候,java.exe被调用,从而根据%JAVA_HOME%\jre\lib\i386\jvm.cfg配置来选择激活jvm,启动之后初始化工作完成之后便产生Bootstrap Loader加载器,它由C++编写。JVM中另外两个内置类加载器是ExtClassLoader和AppClassLoader,它们定义在sun.misc.Launcher.class中,为内部类,且由Bootstrp Loader加载进入虚拟机。
每个类加载器会根据预设的url来搜索.class文件并动态加载之。Bootstrap Loader的加载路径为sun.boot.class.path,表现为CLASS_PATH环境变量;ExtClassLoader加载路径为java.ext.dirs,默认是%JAVA_HOME%\jre\lib\ext;AppClassLoader加载路径为java.class.path,由执行java.exe时的-classpath或-cp参数指定;上述三个类加载器存在下图所示的继承关系:同时,上图还说明了类加载器在Java语言中发挥的很重要的2点作用:
1、类加载器的委派模型:假设AppClassLoader需要加载一个类,它会首先委托其父加载器ExtClassLoader来加载此类,ExtClassLoader也会递归性的委托其父加载器Bootstrap Loader来加载此类,如果Bootstrap Loader在sun.boot.class.path下找到被加载类时即加载,如果无法找到时再依次由子类加载器去加载。委派模型是针对Java安全而设计的,这也印证了Java语言的设计初衷:面向网络的编程语言。 2、由同一个类加载器所加载的类只能引用该加载器和其父加载器所加载的其他类。
Java语言的的动态连接性:经编译后的.class文件对于JVM来说是一个独立的动态链接函数库,类似于Windows 操作系统下的动态联结函数库(Dynamic Linking Library)和Unix下的共享对象(Share Object)。动态连接特性使我们可以在Java程序被编译之后还能对源文件做修改,重新编译修改过的源文件且重启jvm即可更新被加载的.class文件,在Tomcat之类的Web Container下实现的热部署(Hot Deployment)可以在不重启虚拟机的情况下更新.class文件。
另,动态连接性可以显示的实现,有两种方法:
1) Class clazz = Class.forName();
2) 自定义类加载器,然后利用loadClass()加载.class; 上述两种方法可以在运行时动态加载.class文件。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
类加载器是负责加载类的对象。ClassLoader类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。
通常情况下,Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。例如,在 UNIX 系统中,虚拟机从 CLASSPATH 环境变量定义的目录中加载类。
然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。defineClass 方法将一个 byte 数组转换为 Class 类的实例。这种新定义的类的实例可以使用 Class.newInstance 来创建。