下面通过一系列实验来加深对父类委托机制的理解。(首先新建一个Java工程ClassLoaderOrder)
有如下两个Hello.java文件,分别生成.class文件。app版本放到ClassLoaderOrder工程的bin目录下,boot版本放到D:\temp目录下
Java Code
| package com.bjsxt.test; public class HelloLoader { public static void sayLoader(){ System.out.println("I am in app classPath"); } } | | Java Code
| package com.bjsxt.test; public class HelloLoader { public static void sayLoader(){ System.out.println("I am in boot classPath"); } } | |
![](https://img-blog.csdn.net/20170306152504298) | ![](https://img-blog.csdn.net/20170306152549035) |
1、
Java Code
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test0 { public static void main(String[] args) { ClassLoader cl = ClassLoader.getSystemClassLoader(); try { //根据类名HelloLoader去加载类 Class<?> clazz = cl.loadClass("com.bjsxt.test.HelloLoader"); System.out.println(clazz.getClassLoader()); //反射调用静态方法 Method declaredMethod = clazz.getDeclaredMethod("sayLoader", null); declaredMethod.invoke(null, null); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // //如果.java源代码已经在工程中,可直接通过如下方式使用 // Hello.sayLoader(); } } |
![](https://img-blog.csdn.net/20170306152603800)
从应用类加载器开始判断是否加载了Hello,一直到引导类加载器,都没有加载,然后从引导类加载器开始尝试加载,直到应用类加载器加载上。
2、
跟上面代码相同,不过运行的时候配置JVM运行参数:-Xbootclasspath/a:D:/temp/,表示将D:/temp/路径添加到引导类加载器的classpath中。
其中,-Xbootclasspath/a:表示在系统class加载后加载,-Xbootclasspath/p:表示在系统class加载前加载。
![](https://img-blog.csdn.net/20170306152616128)
![](https://img-blog.csdn.net/20170306152626816)
从应用类加载器开始判断是否加载了Hello,一直到引导类加载器,都没有加载,然后从引导类加载器开始尝试加载,加载上。
3、
修改代码如下,依然使用-Xbootclasspath/a:D:/temp/运行
Java Code
| import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test2 { public static void main(String[] args) { ClassLoader cl = ClassLoader.getSystemClassLoader(); try { //读取.class文件字节数组 byte[] classBytes = loadClassBytes("com.bjsxt.test.HelloLoader"); //调用应用类加载器ClassLoader的defineClass()方法 //由于非public所以通过反射来 Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClassMethod.setAccessible(true); defineClassMethod.invoke(cl, classBytes, 0, classBytes.length); //再 //根据类名HelloLoader去加载类 Class<?> clazz = cl.loadClass("com.bjsxt.test.HelloLoader"); System.out.println(clazz.getClassLoader()); //反射调用静态方法 Method declaredMethod = clazz.getDeclaredMethod("sayLoader", null); declaredMethod.invoke(null, null); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // //如果.java源代码已经在工程中,可直接通过如下方式使用 // Hello.sayLoader(); } /** * 将包名中的.替换成路径分隔符,从D:/temp/目录下查找这个文件,如果存在的话,将其读取到字节数组 * @param name * @return */ public static byte[] loadClassBytes(java.lang.String name) { String path="D:/temp/"+name.replace(".", "/")+".class"; InputStream is=null; ByteArrayOutputStream baos=null; try { is=new FileInputStream(path); baos=new ByteArrayOutputStream(); byte[] buffer=new byte[1024]; int ret=0; while((ret=is.read(buffer))!=-1){ baos.write(buffer, 0, ret); } return baos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { if(is!=null) is.close(); if(baos!=null) baos.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } } |
![](https://img-blog.csdn.net/20170306152640348)
先用应用类加载器加载了D:/temp下的Hello类(bootclasspath Hello),然后再次从应用类加载器开始判断是否已经加载Hello,已经加载了直接返回,所以虽然输出的是boot class path,但是ClassLoader是AppClassLoader。
4、
修改如下,继续使用-Xbootclasspath/a:D:/temp/运行
![](https://img-blog.csdn.net/20170306152651504)
![](https://img-blog.csdn.net/20170306152702364)
虽然AppClassLoader已经加载了Hello,但是getParent()扩展类加载器loadClass()的时候,往上开始判断是否已经加载,知道引导类加载器,都没有加载,则引导类加载器开始往下尝试加载,加载上。