JAVA ClassLoader简述

今天学习一下ClassLoader的工作流程,在网上找了些资料,整理如下:
1.ClassLoader简述
JVM在运行时会产生三个ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.
其中,Bootstrap是用C++编写的,我们在Java中看不到它,是null。
Extension ClassLoader用来加载扩展类,即/lib/ext中的类。最后AppClassLoader才是加载Classpath的。ClassLoader加载类用的是委托模型。即先让Parent类(而不是Super,不是继承关系)寻找,Parent找不到才自己找。
三者的关系为:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。
加载一个类时,首先BootStrap先进行寻找,找不到再由ExtClassLoader寻找,最后才是AppClassLoader。
为什么要设计的这么复杂呢?其中一个重要原因就是安全性。比如在Applet中,如果编写了一个java.lang.String类并具有破坏性。假如不采用这种委托机制,就会将这个具有破坏性的String加载到了用户机器上,导致破坏用户安全。但采用这种委托机制则不会出现这种情况。因为要加载java.lang.String类时,系统最终会由Bootstrap进行加载,这个具有破坏性的String永远没有机会加载。
//A.java
public class A{
   public static void main(String[] args){
  A a= new A();
  System.out.println(System.getProperty( "java.ext.dirs"));
  System.out.println(a.getClass().getClassLoader());
  B b= new B();
  b.print();
 }
}
//B.java
  public class B{
   public void print(){
  System.out.println( this.getClass().getClassLoader());
}
1、我们将它放在Classpath中,则打印出
   sun.misc.Launcher$AppClassLoader@92e78c
   sun.misc.Launcher$AppClassLoader@92e78c
  可见都是由AppClassLoader来加载的。
2、我们将其放在%jre%/lib/ext/classes(即ExtClassLoader的加载目录,其加载/lib/ext中的jar文件或者子目录classes中的class文件)中。则会打印出:
  sun.misc.Launcher$ExtClassLoader
  sun.misc.Launcher$ExtClassLoader
3、我们将A.class放到%jre%/lib/ext/classes中,而将B.class放到classpaht中又会怎么样呢?结果是:
  sun.misc.Launcher$ExtClassLoader
  Exception in thread "main" java.lang.NoClassDefFoundError:B
  at A.main(A.java:6)
怎么会这样呢?这其中有一个重要的问题:A类当然是由ExtClassLoader来加载的,B类要由哪个加载呢?B类要由调用它自己的类的类加载器。也就是说,A调用了B,所以B由A的类加载器ExtClassLoader来加载。ExtClassLoader根据委托机制,先拜托Bootstrap加载,Bootstrap没有找到。然后它再自己寻找B类,还是没找到,所以抛出异常。
ExtClassLoader不会请求AppClassLoader来加载!你可能会想:这算什么问题,我把两个类放到一起不就行了?
呵呵,没这么简单。比如JDBC是核心类库,而各个数据库的JDBC驱动则是扩展类库或在classpath中定义的。
所以JDBC由Bootstrap ClassLoader加载,而驱动要由AppClassLoader加载。等等,问题来了,Bootstrap不会请求AppClassLoader加载类啊。
那么,他们怎么实现的呢?我就涉及到一个Context ClassLoader的问题,调用Thread.getContextClassLoader。
2.自定义ClassLoader.
  最简单的ClassLoader就是继承ClassLoader类,多态findClass方法。
/**
*@author guojianjun
*@create date Mar 31, 2010
*/

package testMain;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader {
   private String _classpath;

   public MyClassLoader(String classpath) {
     this._classpath = classpath;
  }

   protected Class<?> findClass(String className)
       throws ClassNotFoundException {
    Class clazz = this.findLoadedClass(className);
     if ( null == clazz) {
       try {
        String classFile = getClassFile(className);
        FileInputStream fis = new FileInputStream(classFile);
        FileChannel fileC = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel outC = Channels.newChannel(baos);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
         while ( true) {
           int i = fileC.read(buffer);
           if (i == 0 || i == -1) {
             break;
          }
          buffer.flip();
          outC.write(buffer);
          buffer.clear();
        }
        fis.close();
         byte[] bytes = baos.toByteArray();
        clazz = defineClass(className, bytes, 0, bytes.length);
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
     return clazz;
  }

   private byte[] loadClassBytes(String className)
       throws ClassNotFoundException {
     try {
      String classFile = getClassFile(className);
      FileInputStream fis = new FileInputStream(classFile);
      FileChannel fileC = fis.getChannel();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      WritableByteChannel outC = Channels.newChannel(baos);
      ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
       while ( true) {
         int i = fileC.read(buffer);
         if (i == 0 || i == -1) {
           break;
        }
        buffer.flip();
        outC.write(buffer);
        buffer.clear();
      }
      fis.close();
       return baos.toByteArray();
    } catch (IOException fnfe) {
       throw new ClassNotFoundException(className);
    }
  }

   private String getClassFile(String clasName) {
    StringBuffer sb = new StringBuffer(_classpath);
    clasName = clasName.replace('.', File.separatorChar) + ".class";
    sb.append(File.separator + clasName);
     return sb.toString();
  }
}
测试类:
/**
*@author guojianjun
*@create date Mar 30, 2010
*/

package po;

public class Student {
   private String stId = "Anran";
   private String stName = "Anran";
   private TClass6 stClass = null;



    

   public TClass6 getStClass() {
     return stClass;
  }

   public void setStClass(TClass6 stClass) {
     this.stClass = stClass;
  }

   public String getStId() {
     return stId;
  }

   public void setStId(String stId) {
     this.stId = stId;
  }

   public String getStName() {
     return stName;
  }

   public void setStName(String stName) {
     this.stName = stName;
  }

    
}
//test
public class TestLoader {
   public static void main(String[] args) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException, SecurityException,
      NoSuchMethodException, IllegalArgumentException,
      InvocationTargetException {
    MyClassLoader a = new MyClassLoader("D:\\");
    Class test = a.findClass( "po.Student");
    Object o = test.newInstance();
    Method m = test.getDeclaredMethod( "getStName", new Class[] {});
    System.out.println(m.invoke(o, new Object[] {}));
  }
}

你可能感兴趣的:(java,jvm,ClassLoader,职场,休闲)