一、目标
通过自定义加载器动态加载一个本地.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
{
Class
<?> aClass
= this.findLoadedClass(name
);
if(aClass
== null
) {
try {
aClass
= this.getParent().loadClass(name
);
} catch (ClassNotFoundException e
) {
}
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,“.”表示当前路径