Java的类加载机制是最容易被忽略的知识,不管是基础,还是往高级开发进阶,都需要了解类的加载机制。
今天从Java中常见的异常 ClassNotFoundException说起,来说说"双亲委托"
我们知道发生这个异常的原因是找不到某个类,但具体是为什么找不到这个类呢?当然最简单的就是当前项目路径下没有这个类。 那么Java是怎么查找项目路径下的类的呢,看看下面这段代码
Alt text
public class Ming {
public static void main(String[] args) {
System.out.println("Ming looking for socker");
Socker socker = new Socker();
}
}
编译运行
$ javac Ming.java Ming.java:7: 错误: 找不到符号 Socker socker = new Socker(); ^ 符号: 类 Socker 位置: 类 Ming Ming.java:7: 错误: 找不到符号 Socker socker = new Socker(); ^ 符号: 类 Socker 位置: 类 Ming 2 个错误
错误很明显,Java在MingHouse下面找不到Socker类,因为Scoker在另一个路径WangHouse下面。眼尖的同学应该会留意到System.out这句日志方法没有报错,说明Java找到了这个类。可是我们没有import,也没有在目录下有这个类,它是系统类,不过Java是怎么找到它的还是得说明一下。
Java类的加载是由几个ClassLoader进行的,他们分别是 · BootStrapClassLoader · ExtensionClassLoader · AppClassLoader 他们之间的区别呢,可以简单理解为加载路径的不同。BootStrap负责加载系统核心类,Extension负责加载扩展类,而AppClassLoader负责加载当前app下面的所有jar和class文件。
还记得JAVA_HOME这个常量吗?通常配置Java环境第一件事就是设置这个常量,因为这样ExtensionClassLoader才能去找到 jre/lib/ext/ 下的jar和class。他们的加载顺序是怎样的呢?就上面代码的例子来说
小明:爸!我找不到袜子! 小明爸爸:找你妈去。 小明:妈!我找不到袜子! 小明妈妈:我也找不到!
在这个例子里,
· 小明->AppClassLoader
· 小明爸爸->ExtensionClassLoader
· 小明妈妈->BootStrapClassLoader
这就是双亲委托
了。
我们可以用这个代码看看当前类是哪个ClassLoader加载的
public class Ming {
public static void main(String[] args) {
System.out.println("class loader : " + Ming.class.getClassLoader());
Socker socker = new Socker();
}
}
输出说明,Ming这个类是由AppClassLoader加载的
class loader : AppClassLoader
总的说,AppClassLoader会现在当前的classpath下找,找不到就问父级ExtensionClassLoader要,再找不到,ExtensionClassLoader就会管BootStrapClassLoader要。实在找不到就会ClassNotFound了.
到这里应该就明白,System类是由ExtensionClassLoader去查找加载,因为已经在JAVA_HOME里有,所以加载的到,而Socker不在ClassLoader的class path中,所以加载不到。
按照双亲委托的机制,我们是没法找到在WangHouse里的Socker的。因为WangHouse的路径并不在我们ClassLoader里。
但是!
我们还是有办法在不import的情况下,通过自定义ClassLoader的方式,来找到WangHouse里的Socker。具体怎么实现呢?
明天我们会接着讲。