一、什么是类加载器?
- 与普通程序不同的是Java程序class文件并不是本地的可执行程序。
- 当运行Java程序时首先运行JVM
- 然后再把Java class加载到JVM里头运行
- 负责加载Java class的这部分就叫做Class Loader。
- 动态的类加载是JVM的一个重要特征,它为Java平台提供了在运行时间安装软件组件的能力。
- JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader
- 和JVM一样BootstrapClassLoader是用本地代码实现的
- 它负责加载核心JavaClass即所有java.*开头的类
- 另外JVM还会提供两个ClassLoader它们都是用Java语言编写的,由BootstrapClassLoader加载
- Extension ClassLoader负责加载扩展的Javaclass例如所有javax.*开头的类和存放在JRE的ext目录下的类
- ApplicationClassLoader负责加载应用程序自身的类。
- java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织
- 在实例化每个类加载器对象时
- 需要为其指定一个父级类加载器对象或默认采用系统类加载器为其父级类加载器
二、加载流程
- 当运行一个程序的时候JVM启动运行bootstrapclassloader
- 该ClassLoader加载java核心API,ExtClassLoader和AppClassLoader也在此时由jvm创建
- 然后调用ExtClassLoader加载扩展API
- 最后AppClassLoader加载CLASSPATH目录下定义的Class
三、类加载器体系
四、类加载器的委托机制
- 每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类,这就是类加载器的委托模式
- 委托机制可以保证内存中只有一份相应类的字节码
- 当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
- 首先,当前线程的类加载器去加载线程中的第一个类
- 如果类A中引用了类B,java虚拟机将实用加载类A的类加载器加载类B
- 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
- 每个类加载器加载类时,又先委托给其上级类加载器
- 当所有祖宗类加载器没有加载到类,回到发起者类加载器
- 还加载不了,则抛出ClassNotFoundException,而不是再去找发起者类加载器的儿子
- 因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
五、简单示例
public class ClassLoaderTest {
public static void main(String[] args){
String name = ClassLoaderTest.class.getClassLoader().getClass().getName();
System.out.println(name);
System.out.println(System.class.getClassLoader());//null就是说是由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
null
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
六、java.lang.ClassLoader类
/**
* 类加载器是负责加载类的对象
* ClassLoader 类是一个抽象类
* 如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。
* 一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
* 数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建
* 数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的
* 如果该元素类型是基本类型,则该数组类没有类加载器。
*/
public abstract class ClassLoader
{
//使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器
protected ClassLoader(){}
//使用指定的、用于委托操作的父类加载器创建新的类加载器
protected ClassLoader(ClassLoader parent){}
***********************************************
//返回委托的系统类加载器。该加载器是新的 ClassLoader 实例的默认委托父类加载器,通常是用来启动应用程序的类加载器
public static ClassLoader getSystemClassLoader(){}
/**
* 使用指定的二进制名称查找类
* 此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。
* 在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用
*/
protected Class<?> findClass(String name)
throws ClassNotFoundException{}
/**
* 使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:
* 调用 findLoadedClass(String) 来检查是否已经加载类。
* 在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
* 调用 findClass(String) 方法查找类。
* 如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。
* 鼓励用 ClassLoader 的子类重写 findClass(String),而不是使用此方法。
*/
protected Class<?> loadClass(String name,
boolean resolve)
throws ClassNotFoundException{}
//调用此方法等效于调用 loadClass(name, false)
public Class<?> loadClass(String name)
throws ClassNotFoundException{}
//将一个 byte 数组转换为 Class 类的实例。必须分析 Class,然后才能使用它
protected final Class<?> defineClass(String name,
byte[] b,
int off,
int len)
throws ClassFormatError{}
//返回委托的父类加载器。
public final ClassLoader getParent(){}
//返回读取指定资源的输入流
public InputStream getResourceAsStream(String name){}
}
七、自定义类加载器
package day27;
import java.util.Date;
public class Demo extends Date{
public String toString()
{
return "你好吗";
}
}
package day27;
import java.io.*;
//自定义的类加载器,有加密功能
public class MyClassLoader extends ClassLoader{
public static void main(String[] args)throws Exception {
String srcFileName = args[0];
System.out.println(srcFileName);
InputStream is = new FileInputStream(srcFileName);
String destFileName = args[1] + srcFileName.substring(srcFileName.lastIndexOf("\\"));
OutputStream os = new FileOutputStream(destFileName);
cypher(is,os);
is.close();
os.close();
}
/**
* 加密
* @param is 输入流
* @param os 输出流
* @throws Exception
*/
private static void cypher(InputStream is,OutputStream os) throws Exception{
int b;
while((b=is.read())!=-1)
{
os.write(b ^ 0xff);
}
}
private String dir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classFileName = dir + "\\" + name.substring(name.lastIndexOf(".")+1) + ".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
bos.close();
byte[] bytes = bos.toByteArray();
System.out.println("my ClassLoader");
return defineClass(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(){
}
public MyClassLoader(String dir){
this.dir = dir;
}
}
package day27;
import java.util.Date;
public class ClassLoaderTest {
public static void main(String[] args) throws Exception{
MyClassLoader loader = new MyClassLoader("mylib");
Date d = (Date) loader.loadClass("day27.Demo").newInstance();
System.out.println(d);
}
}