ClassLoader详解目录1.什么是C LASS L OADER (2)2.C LASS L OADER的层次 (2)3.C LASS L OADER加载C LASS的过程 (5)4.类加载器的顺序 (5)5.JVM是如何来建立类加载器的结构 (6)6.如何实现在运行时的动态载入和更新 (9)7.为什么要扩展C LASS L OADER (14)8.C LASS L OADER树和委托模型 (14)9.U NLOADING?R ELOADING? (15)10.由名字空间引发的 (16)11.类的查询 (18)12.一些重要的方法 (20)13.怎么组装这些方法 (21)14.J AV A 2中C LASS L OADER 的变动 (21)15.类与数据 (22)16.类加载器如何工作? (23)17.名词解释 (27)18.T IPS (27)19.类加载原则概括: (29)20.问题 (29)21.其他 (34)1.什么是ClassLoader与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。
当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM 里头运行,负责加载Java class的这部分就叫做Class Loader。
JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,Bootstrap ClassLoader是用本地代码实现的,它负责加载核心Java Class(即所有java.*开头的类)。
另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由Bootstrap ClassLoader加载;其中Extension ClassLoader负责加载扩展的Java class(例如所有javax.*开头的类和存放在JRE的ext目录下的类),Application ClassLoader负责加载应用程序自身的类。
术语“类加载”指的是找出一个给定类名的字节所在的位置并且将这些字节转换成Java类实例的过程。
2.ClassLoader的层次bootstrap classloader|extension classloader|app classloaderapp classloader 的parent是extension classloader,extension classloader的parent 是bootstrap classloader,bootstrap classloader的parent 是Null。
继承结构:Bootstrap ClassLoader|__URLClassLoader|__ExtClassLoader|__AppClassLoaderBootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。
在Sun的JVM中,在执行java的命令中使用-Xbootclasspath选项或使用- D 选项指定sun.boot.class.path系统属性值可以指定附加的类。
这个加载器的是非常特殊的,它实际上不是ng.ClassLoader的子类,而是由JVM自身实现的。
大家可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:URL[] urls=uncher.getBootstrapClassPath().getURLs();for (int i = 0; i < urls.length; i++) {System.out.println(urls[i].toExternalForm());}在我的计算机上的结果为:file:/D:/jre1.5.0_06/lib/rt.jarfile:/D:/jre1.5.0_06/lib/i18n.jarfile:/D:/jre1.5.0_06/lib/sunrsasign.jarfile:/D:/jre1.5.0_06/lib/jsse.jarfile:/D:/jre1.5.0_06/lib/jce.jarfile:/D:/jre1.5.0_06/lib/charsets.jarfile:/D:/jre1.5.0_06/classes这时大家知道了为什么我们不需要在系统属性CLASSPA TH中指定这些类库了吧,因为JVM在启动的时候就自动加载它们了。
Extension classloader:扩展类加载器,它负责加载JRE的扩展目录(JA V A_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包。
这为引入除Java核心类以外的新功能提供了一个标准机制。
因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的JAR 类包对所有的JVM和app classloader都是可见的。
在这个实例上调用方法getParent()总是返回空值null,因为引导加载器bootstrap classloader不是一个真正的ClassLoader实例。
所以当大家执行以下代码时:System.out.println(System.getProperty("java.ext.dirs"));ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent(); System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());结果为:D:\jre1.5.0_06\lib\extThe parent of extension classloader : nullextension classloader是app classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一个实际的classloader,所以为null。
App classloader:应用(也称为系统)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPA TH 操作系统属性所指定的JAR类包和类路径。
总能通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。
如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。
执行以下代码即可获得:System.out.println(System.getProperty("java.class.path"));输出结果则为用户在系统属性里面设置的CLASSPA TH。
classloader 加载类用的是全盘负责委托机制。
所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;委托机制则是先让parent(父)类加载器(而不是super,它与parent classloader类不是继承关系)寻找,只有在parent找不到的时候才从自己的类路径中去寻找。
此外类加载还采用了cache机制,也就是如果cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。
3.ClassLoader加载Class的过程1.检测此Class是否载入过(即在cache中是否有此Class),如果有到8,如果没有到2。
2.如果parent classloader不存在(没有parent,那parent一定是bootstrap classloader了),到4。
3.请求parent classloader载入,如果成功到8,不成功到5。
4.请求jvm从bootstrap classloader中载入,如果成功到8。
5.寻找Class文件(从与此classloader相关的类路径中寻找)。
如果找不到则到7。
6.从文件中载入Class,到8。
7.抛出ClassNotFoundException。
8.返回Class.。
其中5、6步我们可以通过覆盖ClassLoader的findClass方法来实现自己的载入策略。
甚至覆盖loadClass方法来实现自己的载入过程。
4.类加载器的顺序先是bootstrap classloader,然后是extension classloader,最后才是app classloader。
大家会发现加载的Class越是重要的越在靠前面。
这样做的原因是出于安全性的考虑,试想如果app classloader“亲自”加载了一个具有破坏性的“ng.System”类的后果吧。
这种委托机制保证了用户即使具有一个这样的类,也把它加入到了类路径中,但是它永远不会被载入,因为这个类总是由bootstrap classloader来加载的。
大家可以执行一下以下的代码:System.out.println(System.class.getClassLoader());将会看到结果是null,这就表明ng.System是由bootstrap classloader加载的,因为bootstrap classloader不是一个真正的ClassLoader实例,而是由JVM实现的,正如前面已经说过的。
5.JVM是如何来建立类加载器的结构uncher,顾名思义,当你执行java命令的时候,JVM会先使用bootstrap classloader载入并初始化一个Launcher,执行下来代码:System.out.println("the Launcher's classloader is"+uncher.getLauncher().getClass().getClassLoader());结果为:the Launcher's classloader is null (因为是用bootstrap classloader加载,所以class loader为null)Launcher 会根据系统和命令设定初始化好class loader结构,JVM就用它来获得extension classloader和app classloader,并载入所有的需要载入的Class,最后执行java命令指定的带有静态的main方法的Class。