java类装载机制

来源:互联网 发布:t123ai写软件 编辑:程序博客网 时间:2024/05/16 06:25

当我们在命令行输入java Xxx(某个类)时候,java内部会做些什么动作呢?

Java Xxx
流程如下:
1.         找到JRE;
2.         找到JVM.dll;
3.         启动JVM,并进行初始化;
4.         产生Bootstrap Loader;
5.         载入ExtClassLoader;(Ext – Extended)
6.         载入AppClassLoader;
7.         加载Xxx类。
Bootstrap Loader、ExtClassLoader和AppClassLoader构成了Java的“类加载器继承体系--class loader hierarchy”,其中Bootstrap Loader是由C++编写的,其他两个是由Java写的。之所以成为“继承体系”是因为这三个loader之间是有联系的。Bootstrap Loader负责加载ExtClassLoader,后者ExtClassLoader就将其parent置为Bootstrap Loader。AppClassLoader较为特殊,虽然由Bootstrap载入,但是其parent却置为ExtClassLoader。其原因是为了实现“委托模型”也就是下面所说的“双亲委派模型”简述“委托模型”就是当类加载器有加载类的需求时,会先请求其parent使用其搜索路径帮助加载,如果其parent找不到,才使用自己的搜索路径进行加载。如上述所说当ExtClassLoader想载入AppClassLoader类时它首先请求其parent “Bootstrap Loader”帮忙,Bootstrap Loader将AppClassLoader载入后,由于这个载入是ExtClassLoader请求的,所以AppClassLoader的parent还是置为ExtClassLoader而不是Bootstrap Loader。
 
类的加载流程
类加载的时候遵循一个原则:“类加载器会依类的继承体系从上至下依次加载”。举个例子:“如果C继承了B并实现了接口I,而B有继承自A”,则类加载器在加载C时,加载的次序会是AàBàIàC,(注:interface会如同class一样被Java编译器编译为独立的.class文件)

类装载器:java虚拟机使用每一个类的第一件事情就是将该类的字节码装载进来,装载类字节码的功能是有类装载器完成的,类装载器负责根据一个类的名称来定位和生成类的字节码数据后返回给java虚拟机。最常见的类加载器是将要加载的类名转换成一个.class文件名,然后从文件系统中找到该文件并读取其中的内容,这种类装载器也不是直接将.class文件中的内容原封不动地返回给java虚拟机,它需要将.class文件中的内容转换成Java虚拟机使用的类字节码,譬如,Java程序中的字符串编译成.class文件后是以UTF-8编码存在的,而装载进java
虚拟机后要被转换成Unicode编码。类装载器本身也是一个Java类,允许开发人员自己编写自己的类装载器,以便通过其他各种特殊方式来产生类字节码。我们可以对一些.class文件进行加密处理来防止反编译,但需要使用特殊的类装载器从已被加密处理的.class文件中还原出正常的字节码即可。不管类装载器采用什么方式,只要能够在内存制造出Java虚拟机调用类字节码即可。所以,把类装载器描述为类字节码制造器更容易让人理解。
当一个类被加载后,Java虚拟机将其编译为可执行代码存储在内存中,并将索引信息存储进一个HashTable中,其索引关键字在HashTable中查找相应的信息,如果该代码已经存在,虚拟机直接从内存里调用该可执行代码,反之则调用类装载器并进行加载和编译。
    一个Java类用来描述现实中的事物,Java程序也是一种事物,它可以用一个Java类描述,这个特殊的类名就叫Class。Class类用于描述Java程序语言中使用的一个类的有关信息。可以认为,类装载器装载某个类的字节码的过程实际上就是在创建Class类的一个实例对象,这个Class类的实例对象封装的内容正好是当前加载的类的字节码数据,也就是Java虚拟机对当前加载类编译后存储在内存中的可执行代码!可以采用下面三种方式获得:
    1。类名.class ,例如。System.class
    2。对象.getClass(),例如。new Date().getClass()
    3。Class.forName("类名"),例如,Class.forName("java.util.Date");
    类装载器本身也是一个java类,java类库中提供了一个java.lang.ClassLoader来作为类装载器的基类,java虚拟机和程序都调用ClassLoader的子类。Class类中定义了一个getClassLoader方法,用于返回它所描述的类的类装载器对象,这个返回对象的类型就是ClassLoader.

 双亲委派模型:

在此模型下,当一个装载器被请求装载某个类时,它首先委托自己的parent去装载,若parent能装载,则返回这个类所对应的Class对象,若parent不能装载,则由parent的请求者去装载。

 下面是类装载器使用示例:       

package cn.itcast;
import java.io.*;

public class MyClassLoader extends ClassLoader
{
 private String path = null;  
 public MyClassLoader(String path)
 {
  //错误检查省略
  this.path = path;
 }
 
 
 protected  class findClass(String name)
 {
  
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)
  {
   e.printStrackTrace() ;
  }
  return null;
 }
 
 public static void cypher(InputStream ips , OutputStream ops)
 {
  
try
  {
   int b = 0;
   while((b=ips.read()) != -1)
   {
    ops.write(((byte)b) ^ 0xff);
   }
  }catch(Exception e){}
 }
 
 public static void main(String [] args)
throws Exception
 {
   if(!args[0].endsWith("class"))
   {
    System.out.println(
     MyClassLoader.class.getClassLoader().getClass().getName());
    ClassLoader loader = new MyClassLoader(args[1]);
    ClassLoader ld = loader.getParent();
    while(ld != null)
    {
     System.out.println("     " + ld.getClass().getName());
     ld = ld.getParent();
    }
    Class cls = loader.loadClass(args[0]);
    System.out.println(cls.getClassLoader().getClass().getName());
    java.util.Date d = (java.util.Date)cls.newInstance();
    System.out.println(d.toString());
    return;
   }
  
   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();  
 }
 
}

0 0
原创粉丝点击