Java JVM:编译加载与自定义类加载器

来源:互联网 发布:淘宝有卖寿衣 编辑:程序博客网 时间:2024/06/09 03:09

包括

一. 编译机制 

二. 类加载机制 

三.自定义类加载器


一. 编译机制

        编译主要是把 .java 文件转换为 .class 文件。其中转换后的 .class 文件就包含了元数据,方法信息等一些信息。比如说元数据就包含了 Java 文件中声明的常量,也就是我们所说的常量池。

二. 类加载机制
        JVM 是通过 一个称为 ClassLoader 的东西 来加载 class 文件,每当 JVM 启动的时候,就会产生 三个 ClassLoader,它们分别是Bootstrap Loader, ExtClassLoader 和 AppClassLoader。这三个 ClassLoader 的作用是不同的,它们所加载的class 文件也不同。
  • BootStrap Loader:它所加载的都是 JVM 中最底层的类。
  • ExtClassLoader:用于加载 Java 的一些库。
  • AppClassLoader:它主要由 java.class.path 来指定。
        JVM 加载 class 文件的时候,采用了 双亲委托模型,当需要加载一个class文件的时候,JVM 会首先让这个加载器的父类去加载,如果父类无法加载,就由父类的父类去加载,如果仍然无法加载,那么就由最初的加载器去加载。这三个加载器的委托模式如下图1所示:


图1


        从上图可以看出,每当加载我们自己编写的class 文件的时候,首先会交给 AppClassLoader 来进行加载,如果它不能加载,那么就交给ExtClassLoader 的父类 Bootstrap Loader 进行加载,如果仍然无法加载,那么就交给 AppClassLoader 来进行加载。
        那么,在加载完 class 文件之后, ClassLoader 如何将 class 文件变成一个 java 类的。在 ClassLoader中有一个非常重要的方法叫 findClass() 方法,它接受要加载的类作为它的参数,在该方法中会找到class文件并且读取文件中的内容到一个 byte 数组,然后再调用 另外一个重要的方法:defineClass,该方法能够把 byte 数组中的内容转化为一个相应的 Class Object。

        另外,对于类的加载方法,主要有两种,一种是 隐式的,一种是显式的。
  • 对于隐式的方法:主要使用 new 关键字。
  • 对于显示的方法:可以由  java.lang.Class 的 forName() 方法加载。也可以由 java.lang.ClassLoader的loadClass() 方法加载。
Ps:类加载的委托机制的好处 -- 安全性
        委托机制,指先委托父转载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载类,这一点是从安全角度考虑的, 试想如果有人编写了一个恶意的基础类,比如String类,并装载到JVM中将会引起多么可怕的后果呢。但是,由于有了全盘负责委托机制,String类 永远是有根装载器装载,这样就避免了事件的发生。另外,我们经常遇到的NoSuchMethodError的错误往往就是因为加载了不同版本的包造成的。

三. 自定义类加载器
        默认的类加载器只知道如何从本地系统加载类。如果我们的程序只是在本机跑的话,一般来说默认加载器可以应付。但是如果我们需要加载远程服务器的类,或者说加载网络上的,不是本机上的类的时候,就需要到自定义类加载器。
        
实现方式:

        自定义类加载器可以选择继承ClassLoader类,重写里面的方法来实现。里面有三个重要的方法,一个是loadClass()方法,一个是findClass()方法,一个是defineClass()方法。

        对于loadClass(String name,Boolean resolve)方法:不推荐重写,因为loadClass()方法做的工作主要为实现双亲委托模型,我们重写的话可能会破坏该模型,并且也增加自己的开发难度。

        对于defineClass(String name,byte[] b,int off,int len)方法,主要用于将原始字节转换为Class对象,也不需要我们重写。

        对于findClass(String name)方法,根据名称来查找类。把.class文件的内容放入一个byte[]数组中,供defineClass()方法使用。一般我们就重写这个方法。在这个方法中,我们重新定义从哪里,怎么样读取文件内容。这样我们就可以把从本机上改为从网络上得到类,从而实现自定义类加载器。

总而言之:1.继承ClassLoader类。2.重写findClass(String name)方法。 3.在这个方法里面我们重新定义从哪里,怎么读取文件内容到byte[]数组中,供defineClass()使用。


例子:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class Sample{      
  2.     public int v1 = 1;      
  3.     public Sample(){      
  4.         System.out.println(“Sample is load by :” + this.getClass().getClassLoader());      
  5.     }      
  6. }      
  7. import java.io.ByteArrayOutputStream;      
  8. import java.io.File;      
  9. import java.io.FileInputStream;      
  10. import java.io.InputStream;      
  11.       
  12. public class MyClassLoader extends ClassLoader{      
  13.     private String name;      
  14.     private String path = ”d:\\”;      
  15.     private final String fileType = ”.class”;      
  16.           
  17.     public MyClassLoader(String name){      
  18.         super();      
  19.         this.name = name;      
  20.     }      
  21.     public MyClassLoader(ClassLoader parent, String name){      
  22.         super(parent);      
  23.         this.name = name;      
  24.     }      
  25.     @Override      
  26.     public String toString(){return this.name;}        
  27.     public String getName(){return name;}      
  28.     public void setName(String name){this.name = name;}        
  29.     public String getPath(){return path;}          
  30.     public void setPath(String path){this.path = path;}       
  31.     public String getFileType(){return fileType;}          
  32.           
  33.     protected Class<?> findClass(String name) throws ClassNotFoundException{      
  34.         byte[] data = this.loadClassData(name);       
  35.         return this.defineClass(name, data, 0, data.length);      
  36.     }      
  37.     private byte[] loadClassData(String name){      
  38.         InputStream is = null;      
  39.         byte[] data = null;      
  40.         ByteArrayOutputStream baos = null;      
  41.         try{      
  42.             this.name = this.name.replace(“.”, ”\\”);      
  43.             is = new FileInputStream(new File(path + name + fileType));      
  44.             baos = new ByteArrayOutputStream();      
  45.             int ch = 0;      
  46.             while (-1 != (ch = is.read())){      
  47.                 baos.write(ch);      
  48.             }      
  49.             data = baos.toByteArray();      
  50.         }catch (Exception e){      
  51.             e.printStackTrace();      
  52.         }finally{      
  53.             try{      
  54.                 is.close();      
  55.                 baos.close();      
  56.             }catch (Exception e){      
  57.                 e.printStackTrace();      
  58.             }      
  59.         }      
  60.         return data;      
  61.     }      
  62.     public static void showClassLoader(ClassLoader loader) throws Exception{      
  63.         Class clazz = loader.loadClass(“Sample”);      
  64.         Object object = clazz.newInstance();      
  65.     }      
  66.     public static void main(String[] args) throws Exception{      
  67.         MyClassLoader loader1 = new MyClassLoader(“loader1″);      
  68.         loader1.setPath(“d:\\loader1\\”);      
  69.         MyClassLoader loader2 = new MyClassLoader(loader1, ”loader2″);      
  70.         loader2.setPath(“d:\\loader2\\”);      
  71.         MyClassLoader loader3 = new MyClassLoader(null, ”loader3″);      
  72.         loader3.setPath(“d:\\loader3\\”);      
  73.         showClassLoader(loader2);      
  74.         showClassLoader(loader3);      
  75.     }      
  76. }      


总结:

  1. 编译机制:把 .Java 文件转化为.class文件。
  2. 类加载机制:通过 3个 ClassLoader 来加载 .class 文件,采用双亲委托模型,能达到一定的安全效果。
  3. 自定义ClassLoader:继承ClassLoader类,里面有3个方法,loadClass() - 实现双亲委托模型,findClass() - 定义如何读取文件内容,,defineClass() - 把byte[] 数组内容转化为Class对象;我们需要重写findClass()方法定义如何读取文件内容到byte[]数组。
转载于:http://blog.csdn.net/allen215902/article/details/50681190

0 0
原创粉丝点击