博文主要讲classloader的模型、作用和使用,内容是作者学习java反射机制有关知识时记录的笔记。
ClassLoad:类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
ClassLoader体系结构图
package com.tzx.reflection;
public class MyClassLoader {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(ClassLoaderTest.class.getClassLoader());
System.out.println(System.class.getClassLoader());
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent());
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
}
}
运行结果:
sun.misc.Launcher$AppClassLoader@4aa298b7
sun.misc.Launcher$AppClassLoader@4aa298b7
null
sun.misc.Launcher$AppClassLoader@382f3bf0
sun.misc.Launcher$ExtClassLoader@25082661
null
按照双亲委派机制加载类,每当需要加载一个新的类时,当前的类加载器会先委托其父加载器,查询有没有加载该类。如果父类加载器已近加载该类,那么直接返回加载的class对象,如果没有那么继续向上寻找父类加载器,如果在祖宗类加载器Bootstrap都没有加载该类,那么需要当前的类加载器自己加载,如果当前的类加载器也不能加载则会跑出ClassNotFoundException
异常 (PS:类加载器没有向下寻找,没有getChild只有getParent)。
用这种思想去解析上边代码:Thread.currentThread().getContextClassLoader()指出当前的类加载器是AppClassLoader,需要加载MyClassLoader.class先在父类加载器(ExtClassLoader)中寻找,没有再向祖宗类加载中寻找(Bootstrap ClassLoader),还没有找到那么AppClassLoader自己去加载。
rt.jar.png
像System.java这样的由系统提供的类都在rt.jar中,由Bootstrap ClassLoader加载,由于Bootstrap类加载器不是Java写的,所以打印出来的类名为null。
我们将第一段代码生产的MyClassLoader.class文件打包成jar(java打包成jar|执行jar包中的main方法),放在java_home/jar/lib/ext目录下。
ext.png
再次执行该java程序
im@58user:/usr/lib/jvm/jdk1.8.0_101/jre/lib/ext$ java -cp MyClassLoader.jar com.loadclass.demo.ClassLoaderTest start
sun.misc.Launcher$AppClassLoader@55f96302
sun.misc.Launcher$ExtClassLoader@70dea4e
null
sun.misc.Launcher$AppClassLoader@55f96302
sun.misc.Launcher$ExtClassLoader@70dea4e
null
通过终端输出结果我们可以看到执行ClassLoaderTest程序的类加载器是AppClassLoader,但加载ClassLoaderTest类的类加载器是ExtClassLoader。因为java_home/jar/lib/ext/.jar在执行程序之前就被ExtClassLoader类加载器加载过了。这样避免了类的重复加载~!~!*
package com.tzx.reflection;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoader extends ClassLoader{
static {
System.out.println("MyClassLoader");
}
public static final String driver = "/home/im/Desktop/";
public static final String fileTyep = ".class";
public Class findClass(String name) {
byte[] data = loadClassData(name);
return defineClass(data, 0, data.length);
}
public byte[] loadClassData(String name) {
FileInputStream fis = null;
byte[] data = null;
try {
File file = new File(driver + name + fileTyep);
System.out.println(file.getAbsolutePath());
fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = fis.read()) != -1) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
System.out.println("loadClassData-IOException");
}
return data;
}
}
public class ClassLoaderTest {
public static void main(String[] args) {
MyClassLoader cl1 = new MyClassLoader();
//磁盘中/home/im/Desktop/Hello.class文件存在
try {
Class c1 = cl1.loadClass("Hello");
Object object = c1.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("main-ClassNotFoundException");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}