当在loadClass()
上调用ClassLoader
时,ClassLoader
是否首先检查该类是否已加载,或者是否立即将此检查委托给其父级ClassLoader
?
Java API说:
当请求查找类或资源时,ClassLoader实例会在尝试查找类或资源本身之前将对类或资源的搜索委托给其父类加载器。
但是在Java Reflection in Action一书中有一个关于类加载器的特定章节,它说:
类加载器调用findLoadedClass来检查类是否已经加载。如果类加载器找不到加载的类,则在父类加载器上调用loadClass。
哪个是对的?
适当的类加载器实现将:
ClassLoader.loadClass的默认实现类似于:
protected synchronized Class<?> loadClass(String name, boolean resolve) {
// First, check if this class loader has directly defined the class or if the
// JVM has initiated the class load with this class loader.
Class<?> result = findLoadedClass(name);
if (result == null) {
try {
// Next, delegate to the parent.
result = getParent().loadClass(name);
} catch (ClassNotFoundException ex) {
// Finally, search locally if the parent could not find the class.
result = findClass(ex);
}
}
// As a remnant of J2SE 1.0.2, link the class if a subclass of the class
// loader class requested it (the JVM never calls the method,
// loadClass(String) passes false, and the protected access modifier prevents
// callers from passing true).
if (resolve) {
resolveClass(result);
}
return result;
}
一些类加载器实现将委托给其他非父类加载器(OSGi,例如,根据包委托给类加载器的图形),并且一些类加载器实现将在委托之前在本地类路径中查找类。
Java API是正确的。
当请求查找类或资源时,ClassLoader实例会在尝试查找类或资源本身之前将对类或资源的搜索委托给其父类加载器。
来自Java Classloading Mechanism -
在加载类时,类加载器首先将对类的搜索“委托”到其父类加载器,然后再尝试查找类本身。
这两个陈述并不完全相互排斥。如果父ClassLoader以前未能找到Class,则Class将仅存在于当前ClassLoader的已加载类集中。所以,
当请求查找(描述的是外部数据)类或资源时,ClassLoader实例将在尝试查找之前将对类(或外部数据描述)类或资源的搜索委托给其父类加载器(描述的外部数据) )类或资源本身。
如果它知道它的父节点找不到类但它可以(如之前加载类所示),这不会阻止它短路
这基本上就是它的工作原理。你输入
Foo f = new Foo();
此时,类加载器将确定是否已加载Foo()
,即其在memory / perm gen中的位。如果已加载,则使用它。否则将其委托给父类加载器以尝试解析该类。从磁盘读取该类的位,然后加载到内存中。在下一个new Foo()
上,现在可以在内存/加载中找到该类。
为了同意斯里兰卡的答案,它将始终委托给父母,而api是正确的。如果你正在玩类加载,这可能会让事情变得有点棘手,或者达到你想要的效果。我建议用最小的类路径启动jvm,然后使用自定义类加载器加载所有类,最简单的方法是使用URLClassloader或包装URLClassloader的复合对象,这样你就可以跟踪加载的类和什么时候。
另外值得注意的是,如果C和D不是同一个类加载器 - 父子层次结构的一部分,则由类加载器C加载的类A!=由类加载器C加载的类A.
在这种情况下还应该注意另外一个问题。 API文档说:
由类加载器创建的对象的方法和构造函数可以引用其他类。要确定所引用的类,Java虚拟机将调用最初创建该类的类加载器的loadClass方法。
这意味着引用类的网络由同一个类加载器加载。