类加载器ClassLoader
内置类加载器及其加载路径
- 启动类加载器BootstrapClassLoader:加载
%JAVA_HOME%/jre/bin的类库 - 扩展类加载器ExtClassLoader:加载
%JAVA_HOME%/jre/bin/ext的类库 - 应用类加载器AppClassLoader:加载当前应用的classpath的所有类
ClassLoader.getResource() 可以获取类的绝对路径Class.getResource() 可以获取类的相对路径
在代码中获取当前类加载器和路径
public class JvmPath {public static void main(String[] args) {// 启动类加载器URL[] urLs = Launcher.getBootstrapClassPath().getURLs();System.out.println("Bootstrap类加载器");for (URL urL : urLs) {System.out.println(" ==> "+ urL.toExternalForm());}// 扩展类加载器printClassLoader("扩展类加载器", DemoApplication.class.getClassLoader().getParent());// 应用类加载器printClassLoader("应用类加载器", DemoApplication.class.getClassLoader().getParent());}private static void printClassLoader(String name, ClassLoader cl) {if(cl!=null){System.out.println(name + "ClassLoader ==>" + cl.toString());printUrlForClassLoader(cl);}else{System.out.println(name + "ClassLoader ==> null");}}private static void printUrlForClassLoader(ClassLoader cl) {Object ucp = insightField(cl, "ucp");Object path = insightField(ucp, "path");ArrayList list = (ArrayList) path;for (Object o : list) {System.out.println(" ==> "+ o.toString());}}private static Object insightField(Object obj, String fName) {try {Field f = null;if(obj instanceof URLClassLoader){f = URLClassLoader.class.getDeclaredField(fName);}else{f = obj.getClass().getDeclaredField(fName);}f.setAccessible(true);return f.get(obj);} catch (Exception e) {e.printStackTrace();return null;}}}
类加载器机制
即所谓的双亲委托

- 每次委托会从当前类加载器查找缓存,看类是否已经加载,没有则继续委托父加载器自己先不查找路径
- 到了Bootstrap类加载器也没从缓存中找到,则从自己的加载路径开始找,没找到则交给子加载器再找
- 自定义类加载器通常继承于AppClassLoader,则自定义ClassLoader会先被委托
类加载器启动时机
既然类加载器是用来加载类的,那么自身是什么时候加载类加载器的
- Bootstrap类加载器在JVM启动时启动,来加载核心jar包
- JVM初始化完成后由Launcher创建扩展类加载器和应用类加载器
JVM启动 —> Bootstrap类加载器 Launcher —> Ext类加载器和App类加载器
自定义类加载器
- 集成ClassLoader类
- 重写findClass()方法
- findClass中调用defineClass()方法
