------- android培训、java培训、期待与您交流! ----------
一,类加载器:加载类的工具。
在java程序里,用到一个类,出现类名,JVM首先要把类的字节码加载到内存中,通常类的字节码的原始信息存放在classpath指定目录下,把.classpath加载到硬盘中,再对它进行一些处理,处理成字节码。
把.class文件从硬盘加载进来,然后进行一些处理,这些工作由类加载器完成。
类加载器本身也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。这个类加载器很特殊,它不是java类,不需要被其他类加载器加载,它是嵌套在JVM内核里的,JVM一启动的时候,它就已经在里面了。它是由C++语言写的一段二进制代码。
二,ExtClassLoaer专门加载JRE/lib/ext/*.jar的jar包。
父亲能够找到,则由父亲加载,否则再由子类加载。
每一个类加载器都会有一个父亲,才能挂到JVM的类树上去,组成树状结构。
三,类加载器的委托机制:
①当JVM用到一个类,那么它到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,JVM将使用加载A的类加载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上一级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
②面试题:能不能自己写一个类叫java.lang.System?可以,写了也是白写,自己写了放在classPath下。但是委托机制会委托给祖宗类,祖宗类就会加载上System类,这样总是使用系统提供的System类,永远不会用到自己写的System类。当然可以自己编写类加载器,绕过系统的类加载器。
四,自定义类加载器必须继承一个抽象类ClassLoader。它有一个loadClass方法,这个方法内部会去找它的父类,返回后再执行findClass。子类只需要覆盖findClass方法。
父类ClassLoader-->loadClass/findClass/得到class文件的二进制数据-->使用defineClass方法转换成字节码(也就是class对象)
五,模板方法设计模式:父类中规定大体框架,所有子类都需要实现的代码,写在父类中,各个子类需要实现不同的功能的代码,父类中定义为抽象的,具体实现的由子类实现。
六,类加载器的一个高级问题分析:
新建一个web工程,建一个servlet,打印当前类的类加载器的名字及它的祖辈类。代码如下:
package cn.itcast.itcastweb.web.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ClassLoader loader = this.getClass().getClassLoader();
while(loader!=null){
out.println(loader.getClass().getName()+"<br />");
loader=loader.getParent();
}
out.close();
}
}
部署项目后,运行结果:
org.apache.catalina.loader.WebappClassLoader
org.apache.catalina.loader.StandardClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
可见,当前类是被tomcat自己的类加载器加载的,org.apache.catalina.loader.WebappClassLoader
然后,再做个试验,把刚才的MyServlet导成jar包,放到当前使用的jdk目录下,也就是tomcat使用的jdk目录下,我自己的是C:\Program Files\Java\jdk1.6.0_02\jre\lib\ext\MyServlet.jar,然后再在IE地址栏输入http://localhost:8080/fightingweb/servlet/MyServlet运行,结果报错如下:
如图所示,提示HttpServlet类找不到,分析原因:
先明确一点:如果类A中引用了类B,JVM将使用加载A的类加载器来加载类B。
刚开始,MyServlet是由tomcat自己的类加载器加载的,即用WebAppClassLoader加载的,在加载的过程中发现MyServlet引用了HttpServlet,所以需要加载HttpServlet,WebAppClassLoader类可以成功加载HttpServlet,所以程序正常,没有报错。
后来为什么报错了呢?
后来把MyServlet导出jar包到ext目录下,则使用ExtClassLoader加载,而ExtClassLoader加载HttpServlet的过程中,先委托自己的父类,父类没有找到,所以自己加载,自己又不能加载,所以就报错了。
如何解决呢?
把HttpServlet所在的jar包也放到ext目录下,重启tomcat,就可以了。
运行结果:sun.misc.Launcher$ExtClassLoader