黑马程序员技术交流社区

标题: 老师讲的 Java类加载器 [打印本页]

作者: daoqin    时间: 2014-9-16 22:46
标题: 老师讲的 Java类加载器
本帖最后由 daoqin 于 2014-9-16 22:51 编辑

1,什么是类加载器?
        加载类的工具.

2,类加载器有什么作用?
        当程序需要的某个类,那么需要通过类加载器把类的二进制加载到内存中.
类加载器也是Java类

3,类加载器之间的父子关系和管辖范围.



  1. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();        while (classLoader != null) {
  2.             System.out.println(classLoader.getClass().getName());
  3.             classLoader = classLoader.getParent();
  4.         }
  5.         System.out.println(classLoader);
复制代码



4,类加载器的委托机制:
        1>当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
                    ①首先当前线程的类加载器去加载线程中的第一个类.
                    ②如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器加载类B
                    ③还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类.
         2>每个类加载器加载类时,又先委托给其上级类加载器.
                    ①当所有祖宗类加载器没有加载到类
,回到发起者类加载器,如果还加载不了,则抛出ClassNotFoundException异常,它不会 去找发起者类加载器的儿子,因为没有getChild()方法,即使有,有那么多的儿子交给那一个呢?所以干错就不叫给儿子处理了.               
        委托机制有什么好处?集中管理,如果我们写了几个类加载器,都去加载某个类,那么内存中就有多份这个类的字节码
        能不能自己写一个类叫java.lang.System?
为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸优先,也就是使用的永远是爸爸的(系统的)
System类,而不是我们写的System类.

5,编写自己的类加载器
  1. public static void main(String[] args) throws Exception {
  2.         String srcPath = args[0];
  3.         String destDir = args[1];
  4.         FileInputStream fis = new FileInputStream(srcPath);
  5.         String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
  6.         String destPath = destDir + "\\" + destFileName;
  7.         FileOutputStream fos = new FileOutputStream(destPath);
  8.         cypher(fis,fos);
  9.         fis.close();
  10.         fos.close();
  11.     }

  12.     /**
  13.      * 加密方法,同时也是解密方法
  14.      * @param ips
  15.      * @param ops
  16.      * @throws Exception
  17.      */
  18.     private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
  19.         int b = -1;
  20.         while((b=ips.read())!=-1){
  21.             ops.write(b ^ 0xff);//如果是1就变成0,如果是0就变成1
  22.         }
  23.     }
  24. 然后在新建一个类,通过上面的方法将新建的类的字节码进行加密:
  25. public class ClassLoaderAttachment extends Date { //为什么要继承Date待会再说?
  26.     public String toString(){
  27.         return "hello,itcast";
  28.     }
  29. }
复制代码
并在工程里新建一个文件夹,用来保存加密后的class文件.




那么这就需要使用我们自己的类加载器来进行解密了.
  1. public class MyClassLoader extends ClassLoader{

  2.     public static void main(String[] args) throws Exception {
  3.         String srcPath = args[0];
  4.         String destDir = args[1];
  5.         FileInputStream fis = new FileInputStream(srcPath);
  6.         String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
  7.         String destPath = destDir + "\\" + destFileName;
  8.         FileOutputStream fos = new FileOutputStream(destPath);
  9.         cypher(fis,fos);
  10.         fis.close();
  11.         fos.close();
  12.     }

  13.     /**
  14.      * 加密方法,同时也是解密方法
  15.      * @param ips
  16.      * @param ops
  17.      * @throws Exception
  18.      */
  19.     private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
  20.         int b = -1;
  21.         while((b=ips.read())!=-1){
  22.             ops.write(b ^ 0xff);//如果是1就变成0,如果是0就变成1
  23.         }
  24.     }

  25.     private String classDir;

  26.     @Override
  27.     protected Class<?> findClass(String name) throws ClassNotFoundException {
  28.         String classFileName = classDir + "\\" + name.substring(name.lastIndexOf('.')+1) + ".class";
  29.         try {
  30.             FileInputStream fis = new FileInputStream(classFileName);
  31.             ByteArrayOutputStream bos = new ByteArrayOutputStream();
  32.             cypher(fis,bos);
  33.             fis.close();
  34.             System.out.println("aaa");
  35.             byte[] bytes = bos.toByteArray();
  36.             return defineClass(bytes, 0, bytes.length);
  37.         } catch (Exception e) {
  38.             e.printStackTrace();
  39.         }
  40.         return null;
  41.     }

  42.     public MyClassLoader(){
  43.     }

  44.     public MyClassLoader(String classDir){
  45.         this.classDir = classDir;
  46.     }
  47. }
复制代码
测试运行代码:
  1.    Class clazz = new MyClassLoader("myClass").loadClass("ClassLoaderAttachment");
  2.         //此处不能在使用ClassLoaderAttachment因为一旦用了之后,
  3.         //系统的类加载器就会去加载,导致失败,所以该类就继承了Date类了.
  4.         Date date = (Date)clazz.newInstance();
  5.         System.out.println(date);
复制代码
运行结果:



6,一个类加载器的高级问题:
        我们知道tomcat服务器,是一个大大的java程序,那么它就必须在JVM上运行.
这个大大的java程序内部也写了很多类加载器,它用这些类加载器去加载一些特定的类.注入servlet类.
下面我们新建一个javaweb工程,新建一个servlet程序.
  
  1. public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {
  2.         response.setContentType("text/html");
  3.         PrintWriter out = response.getWriter();
  4.         ClassLoader classload = this.getClass().getClassLoader();
  5.         while (classload != null) {
  6.             out.println(classload.getClass().getName()+"<br>");
  7.             classload = classload.getParent();
  8.         }
  9.         out.println();
  10.         out.close();
  11.     }
复制代码


然后配置服务器,部署应用程序,启动tomcat服务器.在页面访问我们这个servlet,在页面打印的
结果如下图所示:


这是从小到大排序的.
现在呢?我想把该servlet打成jar包,放在ExtClassLoad类加载器加载的路径.
通过Eclipse即可完成





作者: JerryJava    时间: 2014-9-17 06:25
好给力的总结,多谢了
作者: 残羹夜宴丶    时间: 2014-9-17 12:32
马克再说。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2