java:类的加载机制(三)
1.引言
上一节主要记录了下三个类加载器。之所以要学习这些是因为。这些学习完毕之后我会在学习android的动态更新技术,然后写一个demo。这篇博客 深入理解java类加载器。
2.正题
今天主要记录下关于自定义classloader。请参考 深入理解java类加载器。
1.一般尽量不要覆写已有的loadClass(...)方法中的委派逻辑。以下就是冲下了classloader的loadClass方法。
Paste_Image.png的确是报错了。具体情况不清楚,但是先记住,重写classloader千万别重写loadclass方法就好。
2.开发自己的类加载器
重要的:
在前面介绍类加载器的代理委派模式的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用defineClass来实现的;而启动类的加载过程是通过调用loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在Java虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。如类 com.example.Outer引用了类 com.example.Inner,则由类 com.example.Outer的定义加载器负责启动类 com.example.Inner的加载过程。
方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;方法 defineClass()抛出的是 java.lang.NoClassDefFoundError异常。
类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。
在绝大多数情况下,系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输Java类的字节代码,为了保证安全性,这些字节代码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在Java虚拟机中运行的类来。下面将通过两个具体的实例来说明类加载器的开发。
以上的中心思想是:要在堆上形成一个Class。必须经过以下几个步骤:
loadClass---->findClass------>defineClass。
按照参考的博客的文章。我自己写了一个demo。
classloader的代码:
public class MyClassLoad extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[]bytes=getClassData(name);
return defineClass("com.wxy.Test",bytes,0,bytes.length);
}
private byte[] getClassData(String className) {
// 读取类文件的字节
String path = className+".class";
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
// 读取类文件的字节码
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
自定义classload的时候,一定要一步一步的来,不能图俭省;其次可能第一次会报错提示:提示not define错误。原因是define的第一个参数填写错了。。具体的填写方法 可以按照错误提示来。
总结1.新建一个项目,在编译运行的时候,里面的.java文件通过加载,运行等等操作都已经在堆里面形成了Class对象。A a=new A();会先去堆中找有没有这个Class。没得就通过findClass寻找。然后加载。
经过前面的3小章,加上自己写的demo理解。还是很有收获的。下次写内存的分配