代表优先模型
内置的 java 类加载器遵循委托优先模型。这意味着类加载器将允许其父级在尝试加载类之前先加载该类。加载器层次结构的顶部是引导加载器,其次是扩展类加载器和应用程序类加载器。在应用程序类加载器下可以找到 URLClassLoaders 和应用程序创建的任何其他加载器。
引导类加载器可以从 rt.jar 加载文件,其中包含最基本的 java 类,包括 java.lang、java.io、java.util 和 java.net 包中的类。扩展类加载器从 java 安装中的其他 jar 文件加载类。应用程序类加载器加载在类路径中找到的类,并且是应用程序启动时的当前类加载器。
正在加载
那么当应用程序想要加载 HashMap 时会发生什么?要求当前类加载器加载 HashMap 类。在尝试任何操作之前,它会要求其父级扩展类加载器加载该类。扩展类加载器又委托引导类加载器,引导类加载器在 rt.jar 中查找类并加载它。
如果要加载的类在类路径中,则请求将像以前一样转到引导类加载器以检查 rt.jar。引导加载程序无法找到该类,因此该任务将返回到扩展类加载器,该扩展类加载器在 java 安装中搜索该类。当此失败时,任务将恢复到应用程序类加载器,该类加载器扫描类的类路径。
类加载器缓存
实际上,每个类加载器都有一个缓存,其中存储已加载的类,并且在委托给父级之前搜索缓存,但这并不会改变首先委托的原则。
这是检查缓存的地方
Class c = findLoadedClass(name);
URL类加载器
由应用程序创建的 URLClassLoader 将以应用程序 ClassLoader 作为父级。如果它遵循委托优先模型,则将在类路径中提供的 URL 之前找到类。
问题
1)谁加载了我的类
我在您的链接中看到略有不同的代码
309 // First, check if the class has already been loaded
310 Class c = findLoadedClass(name);
311 if (c == null) {
312 try {
313 if (parent != null) {
314 c = parent.loadClass(name, false);
315 } else {
316 c = findBootstrapClass0(name);
317 }
318 } catch (ClassNotFoundException e) {
319 // If still not found, then invoke findClass in order
320 // to find the class.
321 c = findClass(name);
322 }
323 }
如果一个类没有被父类加载,它会抛出一个 ClassNotFoundException 异常,该异常被捕获并允许当前的 ClassLoader 找到该类
321 c = findClass(name);
2)为什么ExtClassLoader没有BootstrapClassLoader作为父级,而是有null?
这是由获取类加载器 API http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getClassLoader()
[getClassLoader()] 返回该类的类加载器。某些实现可能使用 null 来表示引导类加载器。
3)AppClassLoader类扩展UrlClassLoader的目的是什么?
请考虑应用程序类加载器并不特殊,因为它加载用户提供的类,而不是系统类。类路径实际上是一个 URI 列表,因此 URLClassLoader 是一个合适的超类。
参考
有很多关于类加载的文章,包括
- Java 类加载的内部原理 http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
- 甲骨文教程 http://docs.oracle.com/javase/tutorial/ext/basics/load.html