import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * * @author ASUS *MyClassLoader.java * * Sample is loaded bysun.misc.Launcher$AppClassLoader@73d16e93 Dog is * loaded bysun.misc.Launcher$AppClassLoader@73d16e93 Sample is loaded * byloader3 Dog is loaded byloader3 */ public class MyClassLoader extends ClassLoader { private String name;// 类加载器的名字 private String path = "e:\\";// 加载类的路径 private final String fileType = ".class";// class文件的扩展名 public MyClassLoader(String name) { super();// 让系统类加载器成为该类的父加载器 this.name = name; } public MyClassLoader(ClassLoader parent, String name) { super(parent);// 显式指定当前类加载器的父类加载器 this.name = name; } @Override public String toString() { return this.name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } // 自定义类加载器 // 查找并加载类的二进制数据 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = this.loadClassDate(name);// 获得class文件的二进制数组 return this.defineClass(name, data, 0, data.length);// 将字节数组转换为class对象 } // 获得class文件的二进制数组 private byte[] loadClassDate(String name) { InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = null; try { this.name = this.name.replace(".", "\\"); is = new FileInputStream(new File(path + name + fileType)); baos = new ByteArrayOutputStream();// 包装为字节数组输出流 int ch = 0; while (-1 != (ch = is.read())) { baos.write(ch); } data = baos.toByteArray();// 转为字节数组 } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } finally { try { is.close(); baos.close(); } catch (Exception e2) { // TODO: handle exception e2.printStackTrace(); } } return data;// 将class文件的字节数组作为返回值 } public static void main(String[] args) throws Exception { MyClassLoader loader1 = new MyClassLoader("loader1");// loader1的类加载器是系统类加载器 loader1.setPath("e:\\myapp\\severlib\\"); MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");// loader2的类加载器是loader1 loader2.setPath("e:\\myapp\\clientlib\\"); MyClassLoader loader3 = new MyClassLoader(null, "loader3");// loader3的类加载器是根加载器 loader3.setPath("e:\\myapp\\otherlib\\"); test(loader2); test(loader3); } public static void test(ClassLoader loader) throws Exception { // loadClass中调用findClass Class clazz = loader.loadClass("Sample"); Object object = clazz.newInstance(); } }
public class Sample { public int v1 = 1; public Sample() { System.out.println("Sample is loaded by" + this.getClass().getClassLoader()); new Dog(); } }
public class Dog { public Dog() { System.out.println("Dog is loaded by" + this.getClass().getClassLoader()); } }
.class文件存放:
如何运行:
在DOS下运行,因为如果在eclipse下运行,那系统类加载器默认的当前目录中总会包含Sample和Dog的.class文件,这样一来,因为loader1的父类加载器是系统类加载器,测试用loader1加载的时候,它总会使得系统类加载器可以成功加载。因此,将.class文件分别放在了otherlib和severlib中,主函数运行类放在了syslib中,这个文件夹也就称为了系统类加载器的的加载路径。
运行结果:
结果说明:
MyClassLoader 是程序含主函数的类,存放在E:\myapp\syslib路径下,此时默认的系统类加载器的classpath或称为加载路径即为当前目录
Sample 放在E:\myapp\severlib
Dog 放在E:\myapp\severlib
Section1:
test(loader2);
loader2类加载器的组合结构
loader2->loader1->系统类加载器->扩展类加载器->根类加载器
由于扩展类和根类加载器均不能加载Sample类,所以系统类加载器检查是否能加载,因为当前目录也就是系统类加载器的加载目录中不含有Sample,所以由loader1加载,因为loader1中有Sample,所以成功加载,此时loader1即为Sample的定义类加载器,并将Sample的Class对象的引用返回给loader2;Dog同理。
test(loader3);
loader2类加载器的组合结构
Loader3->根类加载器
根类加载器无法加载Sample,所以loader3自行加载,其加载路径中包含Sample所以成功加载
Section2:
此处设置classpath不仅是当前目录E:\myapp\syslib,而且添加了新目录E:\myapp\severlib,因为新路径下包含Sample,所以当一层一层委托父加载器加载时,到系统类加器的时候,可以成功加载Sample;Dog同理;
Section3:
此处先把Dog.class文件放到了当前目录,所以在系统类加载Dog时可以成功加载;而Sample仍有loader1加载
Section4:
此处不仅把Dog.class文件放入了当前目录,还添加了新路径到classpath中,新路径包含Sample和Dog,所以系统类加载器成功加载。
如需补充,待续。。。