Java的ClassLoader加载机制

来源:互联网 发布:白俄罗斯交友软件 编辑:程序博客网 时间:2024/05/16 07:07

一、ClassLoader的概念


Java程序在运行的时候,JVM通过类加载机制(ClassLoader)把class文件加载到内存中,只有class文件被载入内存,才能被其他class引用,使程序正确运行起来.


二、ClassLoader的分类 


Java中的ClassLoader有三种.

1. Bootstrap ClassLoader 

由C++写的,由JVM启动.

启动类加载器,负责加载java基础类,对应的文件是%JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等


2.Extension ClassLoader

Java类,继承自URLClassLoader

扩展类加载器,对应的文件是 %JRE_HOME/lib/ext 目录下的jar和class等


3.App ClassLoader

Java类,继承自URLClassLoader

系统类加载器,对应的文件是应用程序classpath目录下的所有jar和class等


三、ClassLoader的过程


1.三者关系

Java的类加载使用双亲委托机制来搜索类.三种ClassLoader存在父子关系,App ClassLoader的父类加载器是Extension ClassLoader,Extension ClassLoader的父类加载器是Bootstrap ClassLoader,要注意的一点是,这里的父子并不是继承关系.

我新建一个类Test来验证三者的关系.

  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.         ClassLoader ClassLoader1 = Test.class.getClassLoader();  
  5.         ClassLoader ClassLoader2 = ClassLoader1.getParent();  
  6.         ClassLoader ClassLoader3 = ClassLoader2.getParent();  
  7.   
  8.         System.out.println(ClassLoader1);  
  9.         System.out.println(ClassLoader2);  
  10.         System.out.println(ClassLoader3);  
  11.     }  
  12. }  
看下输出结果
  1. sun.misc.Launcher$AppClassLoader@1bbf1ca  
  2. sun.misc.Launcher$ExtClassLoader@1ff0dde  
  3. null  
2.加载机制

当这三者中的某个ClassLoader要加载一个类时,会先委托它的父类加载器尝试加载,一直往上,如果最顶级的父类加载器没有找到该类,那么委托者则亲自到特定的地方加载,如果没找到,那么就抛出异常ClassNotFoundException.这里画张图来表示下


这里要注意一点:只有被同一个类加载器实例加载并且文件名相同的class文件才被认为是同一个class.


四、自定义ClassLoader


1.为什么要自定义ClassLoader

因为系统的ClassLoader只会加载指定目录下的class文件,如果你想加载自己的class文件,那么就可以自定义一个ClassLoader.


2.如何自定义ClassLoader

2.1

新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。

2.2

将class字节码数组转换为Class类的实例

2.3

调用loadClass方法即可


3.例子

我先建一个叫Log的类,很简单,只有一句打印 

  1. public class Log {  
  2.   
  3.     public static void main(String[] args) {  
  4.          System.out.println("加载成功");  
  5.     }  
  6. }  

我把这个java文件放到D盘根目录,然后打开cmd,用javac命令把java文件转化为class文件


然后我新建一个MyClassLoader继承自ClassLoader

  1. public class MyClassLoader extends ClassLoader {  
  2.     @Override  
  3.     protected Class<?> findClass(String name) throws ClassNotFoundException {  
  4.         Class log = null;  
  5.         // 获取该class文件字节码数组  
  6.         byte[] classData = getData();  
  7.   
  8.         if (classData != null) {  
  9.             // 将class的字节码数组转换成Class类的实例  
  10.             log = defineClass(name, classData, 0, classData.length);  
  11.         }  
  12.         return log;  
  13.     }  
  14.   
  15.     private byte[] getData() {  
  16.         //指定路径  
  17.         String path = "D:/Log.class";  
  18.           
  19.         File file = new File(path);  
  20.         FileInputStream in = null;  
  21.         ByteArrayOutputStream out = null;  
  22.         try {  
  23.             in = new FileInputStream(file);  
  24.             out = new ByteArrayOutputStream();  
  25.   
  26.             byte[] buffer = new byte[1024];  
  27.             int size = 0;  
  28.             while ((size = in.read(buffer)) != -1) {  
  29.                 out.write(buffer, 0, size);  
  30.             }  
  31.   
  32.         } catch (IOException e) {  
  33.             e.printStackTrace();  
  34.         } finally {  
  35.             try {  
  36.                 in.close();  
  37.             } catch (IOException e) {  
  38.   
  39.                 e.printStackTrace();  
  40.             }  
  41.         }  
  42.         return out.toByteArray();  
  43.     }  
  44. }  

最后测试一下,输出加载这个Log的class文件的加载器,并且利用反射调用它的mian方法.
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {  
  4.         MyClassLoader myClassLoader = new MyClassLoader();  
  5.         //查找Log这个class文件  
  6.         myClassLoader.findClass("Log");  
  7.         //加载Log这个class文件  
  8.         Class<?> Log = myClassLoader.loadClass("Log");    
  9.           
  10.         System.out.println("类加载器是:"+Log.getClassLoader());    
  11.           
  12.         //利用反射获取main方法  
  13.         Method method=Log.getDeclaredMethod("main", String[].class) ;    
  14.         Object object=Log.newInstance();  
  15.         String [] arg={"ad"};  
  16.         method.invoke(object, (Object)arg);  
  17.     }  
  18. }  

看下打印(有乱码问题,不过可以看出成功了)
  1. 类加载器是:MyClassLoader@29e357  
  2. 鍔犺浇鎴愬姛  


结束语:关于java的classLoader机制我就介绍这么多,后面我打算写下安卓的classLoader机制及热修复再见

原创粉丝点击