简要介绍什么是类加载器和类加载器的作用?
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap ,ExtClassLoader,AppClassLoader
类加载器也是JAVA类,因为其他是Java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是类,这正是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其制定一个父级类装载器对象或者默认采用系统类装载器为其父级加载。
例子一:
public class ClassLoaderTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generatedmethod stub
System.out.println(
ClassLoaderTest.class.getClassLoader().getClass().getName());
//运行后可以知道ClassLoader的加载器是AppClassLoader
System.out.println(
System.class.getClassLoader());
//System 就是一个特殊的类加载器 BootStrap
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
sun.misc.Launcher$ExtClassLoader
null
*/
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,
则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
自定义的类加载器的必须继承ClassLoader
loadClass方法与findClass方法
defineClass方法
编程步骤:
编写一个对文件内容进行简单加密的程序
编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以出了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName.
实验步骤:
对不带包名的Class文件进行加密,加密结果存放到另外一个目录,例如:java
MyClassLoader MyTest.class F:\itcast
运行加载类的程序,结果能够被正常加载,但打印出来的类装载器称为
A ppClassLoader:java MyClassLoader MyTestF:\itcast
用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
父类-->loadClass/findClass/得到class文件的转换成字节码àdefinClass()
子类1()
子类2()
//如果父类不能完成的情况下,子类覆盖父类的方法自己做
API中dedeinclass的例子:
ClassNetworkClassLoader extends ClassLoader{
//创建一个自己的类加载器,一个子类并继承父类
Stringhost;
Intport;
PublicClass findClass(String name){
//复写父类中findClass方法
Byte[]b = loadClassData(name);
//根据loadClassData(name)方法可以取出一个二进制的数据b
ReturndefineClass(name,b,0,b.length);
//把二进制数据b交给definaClass可以得到一个类
}
Private byte[]loadClassData(String name){
//load the class data from theconnection
…
}
}
编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader.
把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。
把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader。
父级类加载器加载的类无法饮用只能被子级类加载器加载的类,原理如下图: