【深入理解java虚拟机v3】 《7.4 类加载器》代码清单7-8 不同的类加载器对instanceof关键字运算的结果的影响

原文例子

package org.fenixsoft.classloading;

import java.io.IOException;
import java.io.InputStream;

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {    //重写了loadClass方法
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    //getClass().getResourceAsStream会尝试从匿名内部类class文件自身所在目录下查找其他类文件
                    //因为是相对路径
                    InputStream is = getClass().getResourceAsStream(fileName);  
                    if (is == null) {
                        //如果代码走至此处,符合双亲委派模型机制
                        return super.loadClass(name); //但是代码永远不会执行到此处
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    //实际执行的是此处代码,即每次都是自定义类加载器自身加载类的
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };
        Object obj = myLoader.loadClass("org.fenixsoft.classloading.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof org.fenixsoft.classloading.ClassLoaderTest);
        //这是我加的第三个打印
        System.out.println(obj.getClass().getClassLoader());
        //这是我加的第四个打印
        System.out.println(org.fenixsoft.classloading.ClassLoaderTest.class.getClassLoader());
    }

}

执行结果:

class org.fenixsoft.classloading.ClassLoaderTest
false
org.fenixsoft.classloading.ClassLoaderTest$1@7852e922
sun.misc.Launcher$AppClassLoader@73d16e93

代码清单7-8中构造了一个简单的类加载器,尽管它极为简陋,main方法内创建一个匿名内部类对象myLoader,重写了loadClass()方法,一般情况下,实现自定义ClassLoader,是不会重写loadClass()方法,因为默认的loadClass()方法是双亲委派模型的基础,因此,myLoader破坏了双亲委派模型(虽然有代码super.loadClass,但永远执行不到),效果就是myLoader是个独立存在的类加载器,和已存在的类加载器(AppClassLoader、ExtClassLoader、启动类加载器)不会存在瓜葛,不会存在误区。

后面会单独写一篇文章,讲述这个误区。

原文解释:

myLoader可以加载与自身在同一路径下(原因是相对路径)的Class文件,我们使用这个类加载器去加载了一个名为“org.fenixsoft.classloading.ClassLoaderTest”的类,并实例化了这个类的对象。

两行输出结果中,从第一行可以看到这个对象确实是类org.fenixsoft.classloading.ClassLoaderTest实例化出来的,但在第二行的输出中却发现这个对象与类org.fenixsoft.classloading.ClassLoaderTest做所属类型检查的时候返回了false。这是因为Java虚拟机中同时存在了两个ClassLoaderTest类,一个是由虚拟机的应用程序类加载器所加载的,另外一个是由我们自定义的类加载器加载的,虽然它们都来自同一个Class文件,但在Java虚拟机中仍然是两个互相独立的类,做对象所属类型检查时的结果自然为false。

笔者注释

原文注释是不是看不懂?下面我来进行详细解释。

我们先从后面2条打印开始分析,第三条打印:

org.fenixsoft.classloading.`ClassLoaderTest$1`@7852e922

表明obj对象的类加载器是我们自定义的匿名内部类ClassLoaderTest$1$1是自动生成的内部类编号:
在这里插入图片描述
如果我们定义一个带名称的子类替匿名内部类,例如

public  class `SelfClassLoader` extends ClassLoader {

那么此时就会输出

org.fenixsoft.classloading.`SelfClassLoader`@7852e922

再来看第四条打印:

sun.misc.Launcher$AppClassLoader@73d16e93

这个比较简单,表明是系统类加载器(也可以称应用程序加载器),这个是JDK自带的。

org.fenixsoft.classloading.ClassLoaderTest.class.getClassLoader()可以理解成main函数所在的类通过import引入了一个其他类A,此时main的加载器和类A一致,这里均为AppClassLoader

类加载器不是双亲委派模型吗?为何这里类加载器是2个不同对象呢?

我们在前文讲过,ClassLoaderTest中的匿名内部类,虽然继承了ClassLoader,但是复写了loadClass方法,而loadClass方法是双亲委派模型的基础,简单来说这个基类中的loadClass方法逻辑是,“当子类加载器尝试加载一个类时,优先让父类帮助自己去加载,如果父类还有父类,那么会让父类的父类去加载,直至最顶层的启动类加载器也失败了,才会让子类自己去加载”

详细可以参考其的文章
类加载器3 ClassLoader中的loadClass(),findClass(),defineClass() 第一章节 《loadClass》源码里面的注释
类加载器2 双亲委派和破坏双亲委派《2.2、双亲委派的实现》章节的源码里面的注释

再看看我们复写的loadClass方法代码呢,直接读取class文件路径信息,去加载文件流,直接加载了,没有委托父类去加载的代码,因此,我们才说,这个自定义的类加载器破坏了双亲委派模型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值