黑马程序员_java之类加载器

类加载器

类加载器:加载类的工具

作用:把class文件加载到内存生成字节码文件

JVM可安装多个类加载器,系统默认3个:BootStrap、ExtClassLoader、AppClassLoader

除BootStrap不是java类,其他类加载也是Java类;BootStrap加载JRE/lib/rt.jar里面的类;ExtClassLoader加载JRE/lib/ext/*.jar

里面的类;AppClassLoader加载classpath指定的所有jar或目录里面的类;我们还可以写自己的类加载器去加载指定的特殊的目录

实例化类加载器对象时,需要指定父级类加载器或默认系统类加载器为其父类加载器

    System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());

    //空指针异常,看前面是否存在,有对象字节码肯定存在。看是否有对象

    //System.out.println(System.class.getClassLoader().getClass().getName());

       System.out.println(System.class.getClassLoader());

       ClassLoader loader=ClassLoaderTest.class.getClassLoader();

       while(loader!=null){

           System.out.println(loader.getClass().getName());

           loader=loader.getParent();

       }

       System.out.println(loader);

类加载器的委托机制

当JVM加载一个类时,到底用哪个类加载器去加载?

首先当前线程的类加载器去加载线程中的第一个类

若类A引用了类B,JVM使用加载A的类加载器去加载类B,还可使用ClassLoader.loaderClass(…)方法指定某个类加载器去加载某个类

每个类加载器加载类时,先委托给其上级类加载器,当所有祖宗类加载器没有加载到类时,返回给发起者加载器,若加载不了,返回

ClassNotFoundException,不再去找发起者类加载器的儿子。

例:能不能写一个java.lang.System类?

通常不行,因为有类加载器委托机制,总是使用java系统提供的System

但是我们可以自己写一个类加载器加载自己写的java.lang.System

自定义类加载器的编写原理分析

模板方法设计模式

父类—>loadClass/findClass/得到class文件的内容转换成字节码-->defineClass

class NetworkClassLoader extends ClassLoader {

         String host;

         int port;

public Class findClass(String name) {

      byte[] b = loadClassData(name);

      return defineClass(name, b, 0, b.length);

     }

private byte[] loadClassData(String name) {}

}

类加密,类加载器解密实例

在cn.cast.day2里有个文件itcastlib,cn.cast.day2里有个类ClassLoaderAttachment

//后期不能使用该类名定义引用变量,编译器无法识别这个类。

public class ClassLoaderAttachment extends Date {

    public String toString(){

       return "hello,itcast";

    }

}

//编写和测试自己编写的解密类加载器

public class MyClassLoader extends ClassLoader {

    public static void main(String[] args) throws Exception {

       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);

       fis.close();

       fos.close();

    }

    private static void cypher(InputStream ips,OutputStream ops)throws Exception{

       int b=-1;

       while((b=ips.read())!=-1){

           ops.write(b ^ 0xff );         

       }

    }

    private String classDir;

    @Override//给子类用,用protected

    protected Class<?> findClass(String name) throws ClassNotFoundException {

       String classfileName=classDir+"\\"+name.substring(name.lastIndexOf('.')+1)+".class";

       try {

           FileInputStream fis=new FileInputStream(classfileName);

           ByteArrayOutputStream bos=new ByteArrayOutputStream();

           cypher(fis,bos);

           fis.close();

           byte [] bytes=bos.toByteArray();

           return defineClass(bytes,0,bytes.length);

       } catch (Exception e) {

           e.printStackTrace();

       }

       return super.findClass(name);

    }

    public MyClassLoader(){  }

    public MyClassLoader(String classDir){

       this.classDir=classDir;

    }

}

//主函数类

Class clazz=new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");

Date d1=(Date)clazz.newInstance();

System.out.println(d1);

/*正常能打印new ClassLoaderAttachment().toString();若用itcastlib文件覆盖ClassLoaderAttachment,则不能正常打印

此时删除classpath里面的ClassLoaderAttachment文件正常打印*/

类加载器的一个高级问题的实验分析

一个web项目的Servlet是由tomcat提供的类加载器加载的,如果我们把一个继承了HttpServlet的Servlet类放到了JRE/lib/ext/目录下,

那么这个Servlet就是由ExtClassLoader类加载器加载了,由于这个Servlet继承了HttpServlet类,所以也需要加载HttpServlet类,但是

此时加载HttpServlet类失败,这是因为类加载器的委托机制,而ExtClassLoader这个类加载器只加载JRE/lib/ext/目录下的类,而

HttpServlet类不位于JRE/lib/ext/目录下,所以加载失败,解决办法是把tomcat提供的servlet.jar包放到JRE/lib/ext/目录下,这时就没

问题了,但是此时加载Servlet类的类加载器是ExtClassLoader这个类加载器了,而不是tomcat提供的类加载器了。

 

posted on 2013-01-28 17:11  念满  阅读(192)  评论(0)    收藏  举报