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操作时调用当前线程的类加载器,还是调用方的类加载器 (三)

posted on 2020-03-01 16:06  silyvin  阅读(326)  评论(0编辑  收藏  举报