java类加载器

类加载器

用到一个类,出现了这个类的名字,那么java虚拟机首先要把这个类的字节码加载到内存里来,通常这个字节码的原始信息放在硬盘classpath指定的目录下,我们把.class里面的内容加载到内存里来,再对他进行一些处理,处理完的结果就是字节码,处理就是类加载器的作用

 

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

 

类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap——用C++写的二进制代码。

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

 

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

 

普通类的类加载器AppClassLoader   System类是由BootStrap加载器加载

AppClassLoader继承ExtClassLoader继承BootStrap

 

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

AppClassLoader专门加载Classpath指定所有jar或目录,ExtClassLoader加载的是JRE/lib/ext/*.jar, BootStrap加载的是JRE/lib/rt.jar ,类加载器有优先级问题,当我自己要写类加载器的时候,要去继承ClassLoader类,然后为其指定加载的目录,自己写的类不能加入委托机制。

 

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

首先当前线程的类加载器去加载线程的第一个类(在线程中有个setContextClassLoader)

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

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

每个类加载器加载类是,又先委托给其上级类加载器

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

 

编写自己的类加载器

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

LoadClass方法与findClass方法(不用重写LoadClass因为这个LoadClass内部回去找其父类,当父类返回以后再接着调用findClass,如果重写LoadClass还要实现找父类这一功能,这是模版方法设计模式)

defineClass方法,把一个二进制数据交给defineClass可以自动转换成Class文件

 

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

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

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

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

 

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

 

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

 

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

package cn.itcast.day2;

 

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 {

                                           

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

                                            }

}

import java.util.Date;

 

public class ClassLoaderAttachment extends Date {

                                            public String toString(){

                                               return "hello,itcast";

                                            }

}

有包名的类不能调用无包名的类

子类不能比父类抛出更广泛的异常

 

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

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

 

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

 

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

 

父级类加载器加载的类无法引用只能被子级类加载器加载的类,

 

Tomcat是一个打的java程序,往虚拟机安装了许多加载器,当把java文件打包打到lib\ext\itcast.jar的时候,在startup.bat里的set JAVA_HOME=jdk的路径tomcat和程序用的要是一个,当把执行的类export到ext里,则这个类继承的HttpServlet所在的jar包也要在这个文件夹下

 

posted @ 2014-02-19 14:24  fred_zhang  阅读(667)  评论(0编辑  收藏  举报