类加载器用来把类加载到JVM中. 从JDK1.2开始,类的加载过程采用父亲委托机制(保证Java平台的安全).在此委托机制中,除了JVM自带的根类加载器外,其余的类加载器都有一个父加载器.
当Java程序请求加载器loader1加载Sample类时,loader1首先委托紫的父加载器去加载Sample类,若父类加载器能加载,则由父加载器完成加载任务,否则才由加载器loader1本身加载Sample类
有两种类型的类加载器
JVM自带的加载器
用户自定义的类加载器
java.lang.ClassLoader
的子类类加载器并不需要等到某个类被"首次主动使用"时再加载它
LinkageError
错误)类加载器就不会报告错误
根类加载器(Bootstrap)–启动类加载器
java.lang.*
等sun.boot.class.path
所指定的目录中加载类库java.lang.ClassLoader
java.lang.ClassLoader
以及其他的java平台类. 当JVM启动时,一块特殊的机器码会执行,它会加载扩展类加载器和系统类加载器,这块特殊的机器码叫做启动类加载器(bootstrap ClassLoader)JRE
正常运行所需的基本组件,这包括java.util
与java.lang
包中的类等等扩展类加载器(Extension)
由启动类加载器加载
java.ext.dirs
系统属性所指定的目录中加载类库jre/lib/ext
子目录(扩展目录)下加载类库java.lang.ClassLoader
的子类系统类加载器(System)—也称为应用类加载器
由启动类加载器加载
classpath
或者系统属性java.class.path
所指向的目录中加载类java.lang.ClassLoader
的子类 除了以上JVM自带的加载器外,用户还可以定制自己的类加载器.Java还提供了抽象类java.lang.ClassLoader
,所有用户自定义的类加载器都应该继承ClassLoader类
在父亲委托机制中,各个加载器按照父子关系形成了树形结构
,除了根类加载器外,其余的类加载器都有且只有一个父加载器
public class MyTest13 {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
while (classLoader != null){
classLoader = classLoader.getParent();
System.out.println(classLoader);
}
}
}
sun.misc.Launcher A p p C l a s s L o a d e r @ 135 f b a a 4 s u n . m i s c . L a u n c h e r AppClassLoader@135fbaa4 sun.misc.Launcher AppClassLoader@135fbaa4sun.misc.LauncherExtClassLoader@2503dbd3
null
public class MyTest18 {
public static void main(String[] args) {
//获取启动类加载器加载class文件路径
System.out.println(System.getProperty("sun.boot.class.path"));
//获取扩展类加载器加载class文件路径
System.out.println(System.getProperty("java.ext.dirs"));
////获取系统类加载器加载class文件路径
System.out.println(System.getProperty("java.class.path"));
}
}
public class MyTest7 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("java.lang.String");
ClassLoader classLoader = clazz.getClassLoader();
System.out.println(classLoader);
Class<?> class2 = Class.forName("main.jvm.classloader.C");
System.out.println(class2.getClassLoader());
}
}
class C{
}
执行结果:
null
sun.misc.Launcher$AppClassLoader@135fbaa4
public class MyTest12 {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass("main.jvm.classloader.CL");
System.out.println(clazz);
System.out.println("+++");
clazz = Class.forName("main.jvm.classloader.CL");
System.out.println(clazz);
}
}
class CL{
static {
System.out.println("CL static block");
}
}
class main.jvm.classloader.CL
+++
CL static block
class main.jvm.classloader.CL
结论:
clazz.getClassLoader
Thread.currentThread().getContextClassLoader()
ClassLoader.getSystemClassLoader
DriverManager.getCallerClassLoader()
class.getClassLoader()
返回)与元素类型的类加载器相同public class MyTest15 {
public static void main(String[] args) {
String[] strings = new String[2];
System.out.println(strings.getClass().getClassLoader());
System.out.println("++++");
MyTest15[] test15s = new MyTest15[2];
System.out.println(test15s.getClass().getClassLoader());
System.out.println("++++");
int[] ints = new int[2];
System.out.println(ints.getClass().getClassLoader());
}
}
执行结果:
null
++++
sun.misc.Launcher$AppClassLoader@135fbaa4
++++
null
对执行结果的说明
String
数组对应的ClassLoader为空是因为String对象是由启动类加载器
加载,故为空int
数组对应的ClassLoader为空是因为int是基本元素类型,其对于的数组没有类加载器,故为空java.lang.Object
类,即,java.lang.Object
这个类会被加载到JVM中java.lang.Object
类,而且这些类之间还是不兼容且不可见(正是命名空间在发挥着作用)binary name
)的类创建额外的命名空间,相同名称的类可以并存在JVM中—只需要用不同的类加载器来加载它们即可类加载器加载路径和用于加载该类的定义类加载器(define class loader
)所共同决定的, 如果同样名字(即相同的完全限定名)的类是由两个不同的类加载器锁加载,那么这些类就是不同的,即便.class
文件的字节码完全一样,并且从相同的位置加载亦如此
在JVMHotSpot
的实现中,系统属性sun.boot.class.path
如果修改错了,则运行会出错,提示如下错误信息:
Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object
每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
当一个类或者一个资源文件存在多个jar中,就好存在jar hell
问题
可以通过一下代码来真的问题
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String resourName = "java/lang/Stirng.class";
Enumeration<URL> urls = ClassLoader.getResource(resourName);
while(urls.hasMoreElements()){
URL url = urls.nextElement();
System.out.println(url)
}