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,所以系统类加载器成功加载。
如需补充,待续。。。