new操作时调用当前线程的类加载器,还是调用方的类加载器 (二)
new操作时调用当前线程的类加载器,还是调用方的类加载器中,有个逻辑问题,显得论据并不充分
根据类加载器顺序-另一种绕开双亲委派的方式,可能以以下的顺序进行
new K();
拿出当前线程类加载器
**查看本加载器缓存native findLoadedClass,无(因为此前没有findClass过,findClass/defineClass作为本地是否有类的缓存的依据类加载器顺序-另一种绕开双亲委派的方式)
调用父加载器系统类加载器加载,有,加载
虽然是由当前线程类加载器率先加载,但最终看上去像是J所在的系统类加载器加载的
无法从现象:
输出: 父 J 加载 父 K 加载
就断定是后者
方式1:
故需要用调试的手段确实,看看是否经过了当前线程类加载器
经过调试,发现率先进入loadClass函数的是AppClassLoader,没有经过当前线程的MyUrlClassLoader,
现有证据做实了
方式2:(莫名风险)
还有种方式,让自定义类加载器先findclass一下(类加载器顺序-另一种绕开双亲委派的方式给出了结论,先findClass,此后再loadClass先会从本地缓存先找,再问父加载器要;但此法会有莫名其妙的问题,不过本例只是findClass一个调用链底层独立类K,可以进行实验,但实际开发中严禁)
那么我们让MyUrlClassLoader先findClass一下K抢注并存起来,此后new K()时调用loadClass,如果是当前线程类加载器MyUrlClassLoader率先加载K,应该会打印“子K加载”,相当于把**的顾虑消除,如果是系统类加载器率先加载K,则仍然会打印“父K加载”
public class J { static { System.out.println("父 J 加载"); String dir = "file:/Users/sunyuming/Documents/tool/jars//MySub-1.0.0-jar-with-dependencies.jar"; URL url = null; try { url = new URL(dir); } catch (MalformedURLException e) { e.printStackTrace(); } URL[] urls2 = {url}; // 若不指定parent参数,则默认由系统类加载器担任自定义类加载器的父加载器,输出parent:sun.misc.Launcher$AppClassLoader@3764951d MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2); Thread thread = new Thread(new Runnable() { @Override public void run() { new K(); 【加载】 System.out.println(System.identityHashCode(K.class)); } }); /** * https://www.cnblogs.com/silyvin/articles/12390264.html * 率先抢注lc3.K,缓存起来 */ try { Class c = myUrlClassLoader.findClass("lc3.K"); System.out.println(System.identityHashCode(c)); } catch (ClassNotFoundException e) { e.printStackTrace(); } thread.setContextClassLoader(myUrlClassLoader); thread.start(); }
输出:
父 J 加载
1828972342
父 K 加载
530178602
与方式一一致,所以,结论是,当new 对象时,并非由当前线程类加载加载(Thread.currentThread().getContextClassLoader())
至于是由new K()所在代码的类加载器(J.class.getClassLoader())加载还是由调用new K()的代码所在的类加载器(Reflection.getCallerClass().getClassLoader())加载,我们看下一回:new操作时调用当前线程的类加载器,还是调用方的类加载器 (三)