类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
如下是类加载器的关系图:
接下来用代码简单说明一下他们的关系
package javatribe.fts.generic;
public class ClassLoaderTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//得到一个类加载器,因为类加载器也是一个类,然后得到类加载器的一个对象
//然后得到这个对象的字节码,根据这份字节码得到类加载器的名字
//输出结果sun.misc.Launcher$AppClassLoaders说明ClassLoaderTest是该类加载器加载的
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
//输出为null不代表它没有类加载器,说明它有一个特殊的类加载器
//是bootstrap类加载器它不是一个java类java对象,它是嵌套在java虚拟机里面的,用c++的二进制实现的
System.out.println(System.class.getClassLoader());
//用代码输出说明三个类加载器的关系
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
//打印顶级加载器的名称
System.out.println(loader);
}
}
最后输出的结果是:
sun.misc.Launcher$AppClassLoader
null
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
对于他们的关系已经在类注释的地方已经说明好了。
用myeclipse的打包工具将ClassLoaderTest输出成jre/lib/ext目录下的fts.jar包,再在myeclipse中运行这个类,运行结果显示为ExtClassLoadr。此时的环境状态是classpath目录有ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,这时候我们就需要了解类加载的具体过程和原理了。
点击Finish之后然后再运行刚才的类,这时候输出另一种结果。这个就说明了三个类加载器的所负责的地方是不一样的。
sun.misc.Launcher$ExtClassLoader
null
sun.misc.Launcher$ExtClassLoader
null
接下来有如下问题:
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再 去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的fts.jar包中后,运行结果为ExtClassLoader的原因。
每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。
那么能不能自己写个类叫java.lang.System,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样总是使用java系统提供的System。所以自己写的System类就加载不到了。那么可不可以自己写一个特殊加载类来加载自己写的System类呢?
如果把先前编写的类加入到jdk的rt.jar中,会有怎样的效果呢?结果是不行!!!结论是是不能随意将自己的class文件加入进rt.jar文件中的。