黑马程序员_加载器简单入门理解

加载器理解

一、类加载器

·简要介绍什么是类加载器和类加载器的作用。

简单说,类加载器就是加载类的工具。当出现一个类,要用到此类的时候,Java虚拟机首先将类字节码加载进内存,通常字节码的原始信息放在硬盘上的classpath指定目录下。

类加载器的作用:将.class文件内容加载进内存进行处理,处理完后的结果就是字节码。

·Java虚拟机可以安装多个类加载器,系统默认三个主要的类加载器,每个类加载器负责加载特定位置的类:

BootStrap,ExtClassLoader,AppClassLoader。

·BootStrap--顶级类加载器:

类加载器本身也是Java类,因为它是Java类,本身也需要加载器加载,显然必须有第一个类加载器而不是java类的,这正是BootStrap。它是嵌套在Java虚拟机内核中的,已启动

即出现在虚拟机中,是用c++写的一段二进制代码。所以不能通过java程序获取其名字,获得的只能是null。

·Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其制定一个父级类加载器对象或者采用默认系统类加载器作为其父级

类加载器。


二、类加载器的委托机制

·当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

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

2)如果类中引用了类B,Java虚拟机将使用类加载器A的类加载器来加载类B。

3)还可以直接调用ClassLoader.loadClass()方法类指定某个类加载器去加载某个类。

·每个类加载器加载类时,又先委托给其上级类加载器。

1)当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,哪有多个儿子,找哪一个呢?

2)对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。


每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类,这就是类加载器的委托模式。类加载器一级级委托到BootStrap类加载

器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类加载器去进行真正的加载。当回退到最初的类加载器时,如果它自己也不能完成类的加载,那就应

报告ClassNotFoundException异常。


有一道面试,能不能自己写个类叫java.lang.System,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样

总是使用java系统提供的System。

把先前编写的类加入到jdk的rt.jar中,会有怎样的效果呢?不行!!!看来是不能随意将自己的class文件加入进rt.jar文件中的。


三、编写自己的类加载器

知识讲解:

1)自定义的类加载器的必须继承ClassLoader

loadClass方法(直接继承,省去委托机制的编写)与findClass方法(覆盖这个就行了)

defineClass方法

2)编程步骤:

编写一个对文件内容进行简单加密的程序。

编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。

编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置

线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。

3)实验步骤:

对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast

运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast

用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。

删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。

    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{  
      
        /** 
         * @param args 
         */  
        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);  
            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;  
      
        //覆盖findClass方法。自己写一个类加载器  
        @Override  
        protected Class<?> findClass(String name) throws ClassNotFoundException {  
            // TODO Auto-generated method stub  
            String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf('.')+1) + ".class";  
            try {  
                FileInputStream fis = new FileInputStream(classFileName);  
                ByteArrayOutputStream  bos = new ByteArrayOutputStream();  
                cypher(fis,bos);  
                fis.close();  
                System.out.println("aaa");  
                //解密  
                byte[] bytes = bos.toByteArray();  
                return defineClass(bytes, 0, bytes.length);  
            } catch (Exception e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            return null;  
        }  
          
        public MyClassLoader(){  
              
        }  
          
        public MyClassLoader(String classDir){  
            this.classDir = classDir;  
        }  
    }  


[java] view plaincopy

        //调用目标类加载器。  
        Class clazz = new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");  
        Date d1 =  (Date)clazz.newInstance();  
        System.out.println(d1);  
    }  



四、一个类加载器的高级问题分析

编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader。

把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。

把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader 。

父级类加载器加载的类无法引用只能被子级类加载器加载的类,原理如下图:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值