张老师为这套视频的读者准备的真不少,以为这集就要学Servlet语法了,张老师为了读者能够学会Servlet确实下了功夫。今天学习了java类装载器对Java类的装载机制,以前真没有理解的这么透彻过,为学习Servlet类的装载机制打好基础。张老师举例子跟着敲了一遍,效果不错。
类装载器,学习Servlet的类装载器,先要看一下Java的类装载器,Java虚拟机使用每个类的第一件事情就是将该类的字节码装载进来,装载字节码的功能是有类装载器完成的,类装载器负责根据一个类的名称来定位和生成类的字节码数据后返回个Java虚拟机。最常见的类加载器是将要加载的类名转换成一个.class文件名,然后从文件系统中找到该文件并读取其中的内容,再转换成Java虚拟机使用的类字节码返回给java虚拟机。类装载器也是一个java类,我们也可以开发自己的类装载器,以便通过其他方式来产生类字节码。当一个类被加载后,java虚拟机将其编译为可执行代码存储在内存中,并将索引信息存储进一个hashtable中,其索引关键字为与之相对应的类名。
java程序中的类本身也是一种事物,他也是用一个Java类描述,这个特殊的类名叫Class,类装载器装载某个类的字节码的过程实际上就是在创建Class类的一个实例对象,这个Class类的实例对象封装的内容正好是当前加载的类的字节码数据。
在程序中获得代表某个类的字节码数据的Class实例对象有三种方式:
类名.class ,对象.getClass() ,Class.forName("类名")
Java提供了一个类java.lang.ClassLoader来作为类装载器的基类,Java虚拟机和程序都调用ClassLoader类的loadClass方法来加载类,ClassLoader是一个抽象类,真正的类装载器必须是ClassLoader的子类。Class类中定义了一个getClassLoader方法,用于返回它所描述的类的类加载器对象,这个返回对象的类型就是ClassLoader。
类装载器的基本策略,
一个类装载器本身也是一个Java类,所以,类装载器自身也需要被另外的一类装载器装载。这有点不好理解了,java虚拟机中内嵌了一个称为Bootstrap的类装载器,它属于java虚拟机的内核,不用类装载器装载。Bootstrap类装载器负责加载java核心包中的类(rt.jar文件中的类),这些类的Class.getClassLoader方法返回值为null。ExtClassLoader类装载器负责加载存放在<java_home>/jre/lib/ext目录下的jar包中的类,AppClassLoader负责加载应用程序的启动执行类。
类装载器的委托模式
一个java虚拟机中所有类装载器采用具有父子关系的树形结果进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象,如果没有指定的话,则以ClassLoader.getSystemClassLoader()方法返回的系统类装载器作为其父级类装载器对象。
系统类装载器通常被设置为启动应用程序的AppClassLoader,可以通过java.system.class.loader系统属性来将系统类装载器设置为其他类装载器。ExtClassLoader是AppClassLoader的父级类装载器。getParent()方法可以得到类装载器的父级类装载器。每个ClassLoader本身只能加载特定位置的和目录中的类,但是,ClassLoader被设计成一种委托模式,使得某一个ClassLoader可以委托它的父级类装载器去加载类,从而让应用程序可以借助某一个子级的ClassLoader去多个位置和目录中进行类的加载。对于一个已被父级类装载器装载的类来说,java虚拟机默认也使用这个父级类装载器去装载他说调用的其他类。一个类装载器不能委托他的子孙类装载器去加载类,一个被父级类装载器装载的类无法调用那些只能被子级类装载器发现和装载的其他类。一个类装载器只能创建一个类的一份字节码数据。
采用委托模式避免了一个java虚拟机中多个类装载器为同一个类创建多份字节码数据的情况。只要开发人员自定义的类加载器不覆盖ClassLoader的loadClass方法(loadClass方法默认将加载委托给父级类装载器),而是覆盖findClass方法,这样就可以继续采用委托模式。