类加载器
Java程序在执行的时候,首先运行JVM(java虚拟机),然后再把class加载到JVM里,负责加载class的这个就叫做类加载器(ClassLoader)。
Java虚拟机中可以安装多个类加载器,默认是三个主要类加载器,每一个负责其指定的位置的类。
三个类加载器加载范围:
BootStrap --> JRE/lib/rt.jar
ExtClassLoade --> JRE/lib/ext/*.jar
AppClassLoader --> CLASSPATH指定的所有jar或目录
加载方式:
自顶向下加载类,自底向上检查是否已加载类。
注意:
类加载器本身也是Java类,但由于类加载器本身也要被类加载器加载,所以第一个类加载器不是java类,是由C++编写的BootStrap 。BootStrap嵌套在Java虚拟机内核中,JVM启动,这个类就会启动。
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,必需指定一个父级类加载器对象或者采用默认系统类加载器为其父级类加载。
例:
获取该类的类加载器及父类、超类的加载器
public class ClassLoaderText {
public static void main(String[] args) {
//获取本类加载器
ClassLoader loader = ClassLoaderText.class.getClassLoader();
while(loader!=null)
{
//获取加载该类的类名字
System.out.println(loader.getClass().getName());
//获取父类加载器
loader = loader.getParent();
}
System.out.println(loader.getClass().getName());
}
}
结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
Exception in thread "main" java.lang.NullPointerException
为什么会报这个异常呢?
因为顶级加载器BootStrap ,不是Java类。
类加载器的委托机制
每个类加载器本身只能加载特定位置下的类,但每个类加载器加载类时,会先委托给其上级类加载器,这就是类加载器的委托机制。类加载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类加载器时,如果它自己也不能完成类的装载,那就报告ClassNotFoundException异常。这里是不会再去找发起者类加载器的儿子,因为没有getChlid方法。
JVM加载一个类的过程:
1,首先是当前线程的类加载器去加载线程中的第一个类。
2,如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B。
3,还可以直接调用 加载器.loadClass(要加载类) 方法来指定某个类加载器去加载某个类。
例:
面试题:能不能自己写个类叫java.lang.System?
答:一般情况下不能,因为类加载采用委托机制,这样让类加载器的父类们优先执行了,也就是说总是使用java系统提供的System。但是我们可以自己写一个类加载器来加载我们自己写的java.lang.System类。
自定义类加载器:
特点:
1,自定义的类加载必须继承ClassLoader
2,覆盖父类findClass方法,并返回defineClass方法
3,类加载器调用loadClass方法来指定去加载那个类
例:
//我的加载器
public MyClassLoader extends ClassLoader{
//覆盖父类findClass方法
protected Class<?> findClass(String name) throws Exception
{
String classFileName = ..要加载的类
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream baos = New ByteArrayOutputStream();
//加密方法
byte [] bytes =baos.toByteArray();
return defineClass(bytes,0,bytes.length);
}
//加密方法
public cypher(InputString is,OutputStream os)
{
加密方法,把每个字符与上oxff即可。
}
}
//须要加密类, 这为什么要继承Date呢?因为在源程序声明的时候,如果加密了,加载器无 法加载
class ClassDemo extends Date
{
}
//源程序
public class LoaderText{
public static void main(String [] args)
{
//指定让我的加载器去加载该类
Class clazz = new MyClassLoader().loadClass(ClassDemo);
//打印是那个加载器在加载,
这里注意,如果上面没有继承Date那么如果这里使用ClassDemo会报错的
因为该类已加密,JVM无法加载,只能由自已的加载器加载。
Date d = (Date)clazz.newInstance();
System.out.println(d);
}
}