首先来了解一下字节码和class文件的区别:
我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的bin目录下)指定的目录下的.class文件,类加载需要将.class文件导入到硬盘中,经过一些处理之后变成字节码在加载到内存中。
下面来看一下简单的例子:
[java] view plaincopyprint?01.package com.loadclass.demo;
02.
03.import java.util.Date;
04.import java.util.List;
05.
06./**
07. * 测试类
08. * @author Administrator
09. */
10.public class ClassLoaderTest {
11.
12. @SuppressWarnings("rawtypes")
13. public static void main(String[] args){
14. //输出ClassLoaderText的类加载器名称
15. System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());
16. System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
17. System.out.println("List类的加载器的名称:"+List.class.getClassLoader());
18.
19. ClassLoader cl = ClassLoaderTest.class.getClassLoader();
20. while(cl != null){
21. System.out.print(cl.getClass().getName()+"->");
22. cl = cl.getParent();
23. }
24. System.out.println(cl);
25. }
26.
27.}
package com.loadclass.demo;
import java.util.Date;
import java.util.List;
/**
* 测试类
* @author Administrator
*/
public class ClassLoaderTest {
@SuppressWarnings("rawtypes")
public static void main(String[] args){
//输出ClassLoaderText的类加载器名称
System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());
System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
System.out.println("List类的加载器的名称:"+List.class.getClassLoader());
ClassLoader cl = ClassLoaderTest.class.getClassLoader();
while(cl != null){
System.out.print(cl.getClass().getName()+"->");
cl = cl.getParent();
}
System.out.println(cl);
}
}
输出结果:
可以看到,ClassLoaderTest类时由AppClassLoader类加载器加载的。下面就来了解一下JVM中的各个类加载器,同时来解释一下运行的结果。
Java虚拟机中类加载器:
Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:
BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为Java类的类加载器本身也是要被类加载器加载的,显然必须有第一个类加载器不是Java类,这个正是BootStrap,使用C/C++代码写的,已经封装到JVM内核中了,而ExtClassLoader和AppClassLoader是Java类。
看一下类加载器的属性结构图:
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象的时候,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载
类加载器的委托机制:
当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?
(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)
(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B
(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类
每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->Ext->ClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。
|
|