考你是不是真懂类加载隔离——插件系统、应用服务器、热重载的基石。
三层内置加载器:Bootstrap(rt.jar,C++原生)、Extension/Platform(jre/lib/ext或模块)、Application(classpath)。
双亲委派:先让父加载器找,父找不到自己才加载。目的:核心类(java.lang.Object)永远来自Bootstrap,防止被冒充。
Tomcat的WebappClassLoader对WEB-INF/classes反着来——webapp自己先加载,这样不同war能在同一个JVM里用不同版本的库。
SPI(ServiceLoader / JDBC Driver)也打破委派,靠Thread.currentThread().getContextClassLoader()——让Bootstrap里的核心代码能找到应用classpath下的实现。
同一个.class被不同classloader加载=两个不同的Class对象,名字一样也会ClassCastException。插件隔离的关键认知。
默认就是双亲委派——Bootstrap→Extension→Application,每层先问父亲、父亲说没有自己才找。保证java.lang.*不会被恶意jar替换掉。两个经典打破:Tomcat的WebappClassLoader对WEB-INF/classes先自己加载,所以不同war能带不同版本的Jackson;SPI靠线程上下文类加载器让Bootstrap里的JDBC API能找到应用classpath下的驱动实现。类的身份=类加载器+全限定名,同一个类两个加载器就是两个Class对象——这也是热重载框架里跨重载持引用会ClassCastException的原因。
Java 9+把Extension换成了Platform(模块化),层级思想一样。
ClassNotFoundException vs NoClassDefFoundError:前者是"从来没找到",后者是"找到过但链接失败或加载器可见性问题"。
即答侠会把这题和Tomcat/SPI实例绑在一起练——面试官一听就知道你搞过插件系统不是背书的。
能,继承ClassLoader重写findClass。插件框架、热重载(JRebel)、OSGi都是这么干的。
DriverManager在rt.jar(Bootstrap),驱动实现在应用classpath(Application)。Bootstrap看不见Application,只能通过线程上下文加载器"向下"去拿。