JAVA_类加载器详解

来源:互联网 发布:快速排序java实现 编辑:程序博客网 时间:2024/06/06 01:11

类加载器

 

一、概述:

 

1、定义:简单说,类加载器就是加载类的工具。

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

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

3、默认类加载器:

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

2BootStrap--顶级类加载器:

类加载器本身也是Java类,因为它是Java类,本身也需要加载器加载,显然必须有第一个类加载器而不是java类的,这正是BootStrap。它是嵌套在Java虚拟机内核中的,已启动即出现在虚拟机中,是用c++写的一段二进制代码。所以不能通过java程序获取其名字,获得的只能是null

 

4Java虚拟机中的所有类加载器采用子父关系的树形结构进行组织,在实例化每个类加载器对象或默认采用系统类加载器作为其父级类加载器。

 

 

 

2类加载器的委托机制

 

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

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

2.如果类A中引用了类BJava虚拟机将使用加载类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

 

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

 

编写自己的类加载器(需要再仔细看一遍,不是太懂)

 

知识讲解:

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

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

defineClass方法

编程步骤:

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

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

编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName

实验步骤:

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

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

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

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

import java.io.*;  

import java.lang.reflect.*;  

public class MyClassLoader extends ClassLoader  

{  

    private String path = null;  

    public MyClassLoader(String path) throws Exception//检查文件是否存在  

    {  

        File f = new File(path);  

        if(!f.isDirectory())  

        {  

            throw new RuntimeException(path + " is not a directory");  

        }  

        this.path = path;  

    }  

      

    public Class findClass(String name) //throws Exception //为什么不能抛出  

    {  

        try  

        {  

            File f = new File(path,name.substring(name.lastIndexOf('.')+1) + ".class");  

            FileInputStream fis = new FileInputStream(f);  

            ByteArrayOutputStream bos = new ByteArrayOutputStream();  

            cypher(fis,bos);  

            byte [] buf = bos.toByteArray();  

            fis.close();  

            bos.close();  

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

        }catch(Exception e)  

        {  

            throw new ClassNotFoundException(name + " is not found!");  

        }  

        return null;  

    }  

  

      

    public static void cypher(InputStream istream,OutputStream ostream) throws Exception  

    {  

        //下面这段代码可能遇到255的字节,当成byte就成了-1  

        /*byte b = 0;  

        while((b = (byte)istream.read()) != -1)  

        {  

            ostream.write(b ^ 0xff);  

        }*/  

          

        int b = 0;  

        while((b = istream.read()) != -1)  

        {  

            ostream.write(((byte)b) ^ 0xff);  

        }  

    }  

  

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

    {  

  

        //下面省略了错误检查  

        if(!args[0].endsWith("class"))  

        {  

            ClassLoader loader = new MyClassLoader(args[1]);  

            Class cls = loader.loadClass(args[0]);  

              

            /*  

            让自定义类继承Date类  

            System.out.println(cls.getClassLoader().getClass().getName());  

                java.util.Date d = (java.util.Date)cls.newInstance();  

                System.out.println(d.toString());  

                */  

                  

            //Method m = cls.getMethod("test",null);//在jdk1.5中报警告,为什么?  

            Method m = cls.getMethod("test");  

            //m.invoke(cls.newInstance(),null);  

            m.invoke(cls.newInstance());  

            //((Test)cls.newInstance()).test();  

            return;  

        }  

        else  

        {         

            FileInputStream fis = new FileInputStream(args[0]);       

            File f = new File(args[1], new File(args[0]).getName());//不用检查目录最后是否有目录分割符  

            FileOutputStream fos = new FileOutputStream(f);       

            cypher(fis,fos);  

            fis.close();  

            fos.close();  

        }  

    }  

}  

  

//类加载器不能加载这种非public的类  

/*  

Exception in thread "main" java.lang.IllegalAccessException: Class MyClassLoader  

 can not access a member of class MyTest with modifiers ""  

*/  

/*  

class MyTest  

{  

    public void test()  

    {  

        System.out.println("hello,www.it315.org");  

    }  

}  

*/  

 


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

 

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

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

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

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






0 0
原创粉丝点击