Java ClassLoader笔记
Java语言系统自带有三个类加载器
- Bootstrap ClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。
- Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
- Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类。
下面是测试BootStrap ClassLoader加载的哪些文件:
/**
* BootStrap ClassLoader 加载的文件
*/
public class Test {
public static void main(String[] args)throws Exception{
System.out.println(System.getProperty("sun.boot.class.path"));
}
}
123456789
输出:
C:\Program Files\Java\jre1.8.0_151\lib\resources.jar;
C:\Program Files\Java\jre1.8.0_151\lib\rt.jar;
C:\Program Files\Java\jre1.8.0_151\lib\sunrsasign.jar;
C:\Program Files\Java\jre1.8.0_151\lib\jsse.jar;
C:\Program Files\Java\jre1.8.0_151\lib\jce.jar;
C:\Program Files\Java\jre1.8.0_151\lib\charsets.jar;
C:\Program Files\Java\jre1.8.0_151\lib\jfr.jar;
C:\Program Files\Java\jre1.8.0_151\classes
下面是测试Extention ClassLoader加载的哪些文件:
/**
* EtxClassLoader 加载文件
*/
public class Test {
public static void main(String[] args)throws Exception{
System.out.println(System.getProperty("java.ext.dirs"));
}
}12345678
输出:
C:\Program Files\Java\jre1.8.0_91\lib\ext;
ClassLoader源码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先查找该加载器是否已经加载了所要加载类
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 如果父加载器不为空就递归调用loadClass,如果父加载器为空就会调用BootstrapLoader,这也说明ExtentionClassLoader的父加载器为null还可以委托BootstrapLoader来加载的原因
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 默认情况下,这个方法实际是在上面说有类加载器都无法找到所要加载类时,抛出异常,throw new ClassNotFoundException(name);
// 也可以自定义ClassLoader覆盖该方法去继续查找所要加载类
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
// 解析所要加载类
resolveClass(c);
}
return c;
}
}
补充:
同一个加载器加载的同源类才是真的同类。不同加载器加载同源类,不是同类!instanceof为FALSE;
Class.forName和ClassLoader.loadClass的区别
// 或者可以用 ClassLoader loader = ClassLoader.getSystemClassLoader();
ClassLoader loader = Thread.currentThread.getContextClassLoader();
//这句话没有执行初始化,其实与Class.forName("xx.xx",false,loader)是一致的,只是loader.loadClass("xx.xx")执行的是更底层的操作
Class cls = loader.loadClass("xx.xx");
// 执行这句才进行初始化
cls.NewInstance();
补充:
-
是否被初始化:
Class.forName(className) 实际上是调用Class.forName(className, true, this.getClass().getClassLoader()。注意第二个参数,是指Class被loading后是不是必须被初始化。 ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指出Class是否被link。 区别就出来了。Class.forName(className)装载的class已经被初始化,而ClassLoader.loadClass(className)装载的class还没有被link。
-
forName支持数组类型,loadClass不支持数组:
一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。 例如,在JDBC编程中,常看到这样的用法,Class.forName(“com.mysql.jdbc.Driver”),如果换成了 getClass().getClassLoader().loadClass(“com.mysql.jdbc.Driver”),就不行。
32万+

被折叠的 条评论
为什么被折叠?



