一、目标
通过自定义加载器动态加载一个本地.class类文件,得到该类的class对象
二、代码分析
package com.lyz.classloader;
import java.io.*;
public class FileClassLoader extends ClassLoader {
private String rootPath;
public FileClassLoader(String rootPath) {
this.rootPath = rootPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//查看名为“name”的类是否被加载
Class<?> aClass = this.findLoadedClass(name);
if(aClass == null) {
try {
//双亲委派机制
aClass = this.getParent().loadClass(name);
} catch (ClassNotFoundException e) {
//异常可以忽略
//e.printStackTrace();
}
if(aClass == null) {
byte[] buffer = getClassData(name);
aClass = defineClass(name, buffer, 0, buffer.length);
} else {
return aClass;
}
} else {
return aClass;
}
return aClass;
}
private byte[] getClassData(String name) {
String pathClass = rootPath +File.separator+ name.replace(".",File.separator)+".class";
InputStream inputStream = null;
ByteArrayOutputStream baos = null;
try {
inputStream = new FileInputStream(pathClass);
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len=inputStream.read(buffer))!=-1) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (inputStream!=null)
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null)
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.1为什么要使用ByteArrayOutputStream流
下面解释以下getClassData方法中为什么要用到ByteArrayOutputStream流,因为数组在定义的时候就必须确定长度,而我们如果不使用ByteArrayOutputStream流的toByteArray方法,就无法返回一个长度恰好等于.class文件的字节数组。
2.2 四个方法loadClass,findClass,findloadClass,defineClass
2.2.1findloadClass(String name)方法
查找已加载过的名为name(注:带包的全类名)的类,返回对应的class对象
2.2.2findClass(String name)方法
查找名为name的类,返回对应class对象
2.2.3defineClass(String name,byte[] b,int off,int len)
把字节数组b中的内容转换为java类,返回的结果是对应的class对象。name是全类名,b是已经编译好的.class文件转换成的字节数组,off与len限定了读入的字节数组长度
2.2.4loadClass(String name)
加载名为name的类,此方法定义在ClassLoader类中,采用了双亲委托机制,首先在已加载的类中寻找,然后Bootstrap ClassLoader中,再Extension ClassLoader,再Application ClassLoader,最后才是自定义的加载器
,若要改变加载顺序,则重写此方法,但是不建议。
三、测试
package com.lyz.classloader;
public class Text {
public static void main(String[] args) {
FileClassLoader fcl = new FileClassLoader("f:");
Class<?> aClass1 = null;
try {
aClass1 = fcl.loadClass("com.lyzh.Text");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(aClass1.hashCode());
System.out.println(aClass1.getClassLoader());
}
}
注意,运行程序之前,在路径"F:\com\lyzh"下已经存在Text.class文件
带包的Text类编译时,javac -d . Text.class,“.”表示当前路径