首先ClassLoader分为:
Bootstrap ClassLoader/启动类加载器
主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。
Extension ClassLoader/扩展类加载器
主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。
System ClassLoader/系统类加载器
主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。
User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。
关系如下:

具体类的实现关系如下:

自定义类加载器加载一个类的步骤步骤,使用的双亲委托加载模式:

为什么要使用这种双亲委托模式呢?
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。
下面写一段代码用于观察其加载过程:
public class ClassLoaderTree { public static void main(String[] args) { ClassLoader loader = ClassLoaderTree.class.getClassLoader(); while (loader != null) { System.out.println(loader.toString()); loader = loader.getParent(); } } }
结果观察一下,看看是不是跟想的一样。
上网去查classloader的资料,都叫我们自己如果要自定义classloader用于加载的类话,就只需重写findclass就行了,为什么了?自己看看了ClassLoader源码后,写了一个简易版的,很好理解。
import java.util.Hashtable; import java.io.FileInputStream; public class SimpleClassLoader extends ClassLoader { private Hashtable classes = new Hashtable(); public SimpleClassLoader() { } /** * * 函数用于查找返回自定义class文件的二进制文件 */ private byte findclass(String className)[] { System.out.println(" >>>>>> 查找 返回类 "+className); byte result[]; try { FileInputStream fi = new FileInputStream("store\\"+className+".impl"); result = new byte[fi.available()]; fi.read(result); return result; } catch (Exception e) { /* * If we caught an exception, either the class wasnt found or it * was unreadable by our process. */ return null; } } /** * loadClass 用于加载类(根据类名字符串) */ public Class loadClass(String className) throws ClassNotFoundException { return (loadClass(className, true)); } /** * 加载类的具体过程 */ public synchronized Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException { Class result; byte classData[]; System.out.println(" >>>>>>加载 class : "+className); /* Check our local cache of classes */ result = (Class)classes.get(className); if (result != null) { System.out.println(" >>>>>> 返回已经加载的class."); return result; } /* Check with the primordial class loader */ try { result = super.findSystemClass(className); System.out.println(" >>>>>> 从CLASSPATH中返回已经加载的类"); return result; } catch (ClassNotFoundException e) { System.out.println(" >>>>>> CLASSPATH中没有这个类."); } /* 自定读取class */ classData = findclass(className); if (classData == null) { throw new ClassNotFoundException(); } /* 定义类 (分析 class 文件) */ result = defineClass(classData, 0, classData.length); if (result == null) { throw new ClassFormatError(); } if (resolveIt) { resolveClass(result);//解析class文件 } classes.put(className, result); System.out.println(" >>>>>> 返回 这个新加载 class."); return result; } }