--------android培训、java培训、期待与您交流! ----------
黑马程序员---高新技术之类加载器
1、类加载器的概念
(1).类加载器是加载类的工具,它的作用是将硬盘上的.class文件加进内存,并对之进行一些处理。
(2)java虚拟机中可安装多个类加载器,系统默认三个主要的类加载器,每个类加载器负责加载特定的类:
BootStrap xitClassLoader AppClassLoader
(3).类加载器也是java类,因为其他是java类的类加载器也要被类加载器加载,显然必须有第一个类加载器不是java类, 这正是:BootStrap,它是由C++编写的。
(4).java虚拟机中的所有类加载器采用具有 父子关系 的 树形结构 进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象,或者默认采用系统类加载器为其父级类加载器。
2、类加载器之间的父子关系和管辖范围图
|---Bootstrap ClassLoader ->JRE/lib/rt.jar
|---Extension ClassLoader ->JRE/lib/ext/*.jar
|---ApplicationClassLoader ->ClassPath指定的所有jar或目录。
|--MyClassLoader ->指定的特殊目录
|--itcastClassLoader->指定的特殊目录
3、类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
4、编写自己的类加载器。
知识讲解:
(1).自定义的类加载器必须继承ClassLoader
(2)loadClass方法与findClass方法:只需重新findClass方法,就会跳出委托机制。
(3)defineClass方法。
编程步骤:
(1).编写一个队文件内容进行监督加密的程序
(2).编写了一个自己的类加载器,可实现对加密的类进行撞在和解码。
(3)编写一个程序调用类加载器加载类,在源程序中不能用该类定义引用变量,因为编程器无法识别这个类。程序中可以除了使用C
lassLoader.load方法外,还可以使用使用设置线程的上下文加载器或者系统加载器,然后再使用Class.forName.
//要加载的测试类代码:
import java.util.Date;
public class ClassLoaderAttachments extends Date {
@Override
public String toString() {
return "hello,java";
}
}
//注意:这里最好继承一个类,以便于获取其实例时,定义父类的引用,而不是它自己的引用。
//编写自己的类加载器代码:(里边包含main方法,以便于对类加密解密)
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName =srcPath.substring(srcPath.lastIndexOf('\\')+1);
String destPath = destDir+"\\"+destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis, fos);
System.out.println("xxxxx");
fis.close();
fos.close();
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName = classDir+"\\"+name+".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte[] b = bos.toByteArray();
return defineClass(b, 0, b.length);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("aaaa");
return super.findClass(name);
}
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
public static void cypher(InputStream ips,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b^0xff);
}
//这时候不用关闭,谁创建资源,谁关闭资源。
}
}
//调用自己写的类加载器
Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachments");
Date d = (Date)clazz.newInstance();
System.out.println(d.toString());
5、类加载器的一个高级问题
当把我们写好的MyServlet作为jar包导入的Tomcat使用的JDK的jre/lib/ext中时,这时的MyServlet的类加载器由原来tomcat的WebAppClassLoader变成了ExtClassLoader,在加载TMyServlet时,又用到了HttpServlet,ExtClassLoader找不到就不会报错此时,只需将tomcat中包含HttpServlet的servlet-api.jar也导入ext文件夹下即可。
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ClassLoader loader = this.getClass().getClassLoader();
while(loader!=null){
out.println(loader.getClass().getName()+"<br>");
loader=loader.getParent();
}
out.close();
}
}