目录
1.启动类加载器(Bootstrap ClassLoader)
2.扩展类加载器(Extensions ClassLoader)
3.应用程序类加载器(Application ClassLoader)/系统类加载器
四、Class.forname()与ClassLoader.loadClass():
类加载器的任务就是根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例。
一、从Java虚拟机的角度来看
只存在两种不同的类加载器:
- 一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++实现,是虚拟机本身的一部分;
- 另一种就是所有其他类的加载器,这些类都是由Java语言实现的,独立于虚拟机之外,并且全部都继承自抽象类java.lang.ClassLoader。
二、从Java开发人员角度看
1.启动类加载器(Bootstrap ClassLoader)
它用类加载java 的核心库(String 、Integer、List。。。)在jre/lib/路径下的内容,如:rt.jar,是用C代码来实现的,并不继承自java.lang.ClassLoader。
2.扩展类加载器(Extensions ClassLoader)
用来加载java的扩展库<JAVA_HOME>\lib\ext目录下的内容,java虚拟机的实现会自动提供一个扩展目录。该类加载器在此目录里面查找并加载java类。
3.应用程序类加载器(Application ClassLoader)/系统类加载器
它负责加载java应用的类路径(classpath路径),一般来说,java应用的类都是由他来完成加载的;另外这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器。
4.自定义类加载器
开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
三、类加载器双亲委派模型
如图:
类加载器的代理模式:双亲委托机制
就是某个特定的类加载器在接收到加载类的请求后,首先将加载任务委托给父类加载器,一次追溯,直到最高的爷爷辈的,如果父类加载器可以完成类加载任务,就成功返回;只要父类加载器无法完成次加载任务时,才自己加载。
这样的好处是:java类随着它的类加载器一起具备了带有优先级的层次关系.这是十分必要的,比如java.langObject,它存放在\jre\lib\rt.jar中,它是所有java类的父类,因此无论哪个类加载都要加载这个类,最终所有的加载请求都汇总到顶层的启动类加载器中,因此Object类会由启动类加载器来加载,所以加载的都是同一个类,如果不使用双亲委派模型,由各个类加载器自行去加载的话,系统中就会出现不止一个Object类,应用程序就会全乱了.
ClassLoader中的主要源码:
//如下逻辑是,先通过findClass查找该类是否已经加载了,如果没有加载再通过父类的laodClass去加载,
//如果父类加载失败了,在调用自身的findClass去加载。
//当然如果父类为null,说明此时父类是BootStrap,所以调用native方法findBootstrapClassOrNull来加载
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
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();
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;
}
}
四、Class.forname()与ClassLoader.loadClass():
Class.forname():是一个静态方法,最常用的是Class.forname(String className);根据传入的类的全限定名返回一个Class对象.该方法在将Class文件加载到内存的同时,会执行类的初始化.
如: Class.forName("com.vison.User");
ClassLoader.loadClass():这是一个实例方法,需要一个ClassLoader对象来调用该方法,该方法将Class文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化.该方法因为需要得到一个ClassLoader对象,所以可以根据需要指定使用哪个类加载器.
如:ClassLoader classLoader =.......;cl.loadClass("com.vison.User");
参考:<深入理解java虚拟机>