一、简介
Java类加载器(英语:Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。
类加载器安装在JVM(java虚拟机)中,可以安装多个,系统默认三个主要类加载器:每个类加载器负责加载特定位置的类:
1、BootStrap:非Java类,嵌入在JVM内核中,随着JVM启动而启动,是顶层类加载器,由C++编写,负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class
2、ExtClassLoader:Java类,负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-D java.ext.dirs指定目录下的jar包
3、AppClassLoader:Java类,负责加载classpath中指定的jar包及目录中class
4、自定义类加载器
范例一,获得自定义类的加载器名称
package com.itcast.classloader;
/**
* 类加载器
* @author Administrator
*
*/
public class ClassLoaderTest {
public static void main(String[] args) {
//获取加载该类的加载器名称:sun.misc.Launcher$AppClassLoader
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
}
}
JVM中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父类加载器对象或者默认采用系统类加载器为其父类加载器,具体参加java.lang.ClassLoader类中构造方法。其层次结构图以及加载的位置如下图所示。
在上图中检查类是否加载是从子类到父类进行检查,一旦查到需要使用的类已经加载,则停止检查,使用该类。这样的检查机制似乎我们可以理解,但是为什么在加载类的时候要从父类开始呢?
其实这是由类加载器的委托机制决定的,试着想想,如果我们需要加载类A,但是此时我们没有一个类加载机制,让加载器都能够有机会加载类,那么在JVM中会不会出现多份A的Class呢?这是可能存在的,但是这与我们的想法是相违背的,我们只需要加载一份A的Class就OK了。所以为了解决这个问题,Java对类加载器设立了委托机制,当需要一个类的Class时,指定加载该类的类加载器会首先将加载的要求委托给其上层的加载器,一层层的向上委托,并在委托的过程中检查该类的Classs是否存在,存在则停止,使用该类;不存在一直委托到顶层加载器停止,然后由顶层尝试加载,如果可以加载,则停止,使用该类;否则将从顶层加载器处原路返回,直到加载到该类。如果已经返回到发起者加载器还未加载到,则抛出ClassNotFoundException异常。
总结:
1、当JVM要加载一个类时,到底派出哪一个类加载器去加载类?(JVM加载类过程:装载(Load),链接(Link)和初始化(Initialize))
首先当前线程的类加载器去加载线程中的第一个类。
如果类A引用了类B,JVM将使用加载类A的类加载器来加载类B
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
2、每个类加载器加载类时,又委托给其上级类加载器
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再去找发起者加载器的子加载器,因为没有getChild方法,即使有,那有多个子加载器,找哪一个?
二、自定义类加载器
1、ClassLoader.loadClass()的加载步骤为:
第一步: 调用 findLoadedClass(String)
来检查是否已经加载类。
第二步: 在父类加载器上调用 loadClass
方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
第三步:调用 findClass(String)
方法查找类。
自定义的加载器可以覆盖该方法loadClass(),以便定义不同的加载机制。一旦覆盖了该方法,开发者可以通过自己的方式实现类的加载,放弃委托机制;也可以实现自己逻辑的同时仍然沿用类加载器的委托机制。
2、findClass:是loadClass的最后一步,如果仅是覆盖这个方法实现自定义类加载器,那么仍然沿用委托机制。
indClass()的功能是找到class文件并把字节码加载到内存中。
自定义的ClassLoader一般覆盖这个方法。——以便使用不同的加载路径。
自定义类加载器实例:
package com.itcast.classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
/**
* 自定义类加载器
* 1、必须继承ClassLoader抽象类
* @author Administrator
*
*/
public class CustomClassLoader extends ClassLoader {
public static void main(String[] args) throws Exception {
//加载类,获取其class
Class clazz=new CustomClassLoader().loadClass("com.itcast.classloader.ClassLoaderTest");
//实例化该类,并输出其信息
System.out.println(clazz.newInstance().toString());
}
/**
* findClass()的功能是找到class文件并把字节码加载到内存中。
* 自定义的ClassLoader一般覆盖这个方法。——以便使用不同的加载路径。
* 在其中调用defineClass()解析字节码。
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
FileInputStream fis=new FileInputStream("E:\\workspace\\JavaEnhance\\bin\\com\\itcast
\\classloader\\ClassLoaderTest.class");
ByteArrayOutputStream bos=new ByteArrayOutputStream();
int b=-1;
while((b=fis.read())!=-1){
bos.write(b);
}
byte[] bytes=bos.toByteArray();
fis.close();
bos.close();
return defineClass(name, bytes,0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
package com.itcast.classloader;
/**
* 类加载器
* @author Administrator
*
*/
public class ClassLoaderTest {
@Override
public String toString() {
return "ClassLoaderTest.....";
}
}