作者:毕来生
微信:878799579
1、小故事理解类加载器以及双亲委派模型
首先我们来描述一个小说场景,通过这个场景在去理解我们相关的类加载器的执行以及双亲委派模型。
上古时代有逍遥派和万魔宗两个宗派,互相对立。逍遥派比万魔门更加强势。巅峰战力更高。
有一天万魔宗一名长老之子的仆人外出猎物期间杀掉了一小队逍遥派历练弟子。可惜手脚不干净,留下了线索。被逍遥派探子发现了自己师弟师妹被杀。消息传回宗门后。
逍遥派收到此消息后大怒,发出战书。定要万魔门给个交代。万魔门宗主收到战书后一脸莫名其妙,这么点小事也来烦我?不知道我修炼有多重要吗?不就是杀了几个人么?遂叫来大长老,“大长老你看看这战书,这么点事情还办不好还要我亲自来处理?要你有什么用 ? 你去准备点东西把这件事情处理一下。退下吧!”
大长老灰溜溜的回去。叫来自己的儿子。上去就抽了一巴掌,看看你办的好事。我不管了。你自己想办法解决,解决不了就别回来了。
大长老之子哭丧着脸回到了自己的府邸。从自己的宝库挑出一些珍宝前去拜访逍遥门,看能不能通过这些珍宝解决这件事情。结果,到了逍遥派门前,守卫一看就这么点东西就想交代。对万魔门大长老之子说:"赶紧滚关进滚,就这么点东西还想给我们交代?把杀了我们师弟师妹的人带过来,不然这事儿不算完。“
大长老之子那叫一个憋屈呀,宝物什么的他们也不要,看样子他们是铁了心想要哪些杀了他们的罪魁祸首。那就把他们交出去把。他们惹的事。让他们自己解决。遂派人抓捕这些人送往逍遥派,此事才得以平息。
2、小故事角色行为分析
下面我们通过一张关系图来分析一下我们的故事场景以及对应我们类加载器之间的关系
通过这个图我们可以了解到我们的小故事中与我们Java中的类加载器的对应关系。
1、宗主:引导类加载器。这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。
2、长老:拓展类加载器。,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发人员可以直接使用标准扩展类加载器。
3、长老之子:系统类加载器。,它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader类中的getSystemClassLoader()方法可以获取到该类加载器。
4、长老之子的下人:自定义类加载器。,它需要如下步骤才可以实现自定义效果
4.1. 继承java.lang.ClassLoader
4.2. 重写findClass()方法
4.3 调用defineClass()方法
双亲委派模型
1、什么是双亲委派模型?
特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
2、双亲委派模型是如何使用的?
1)我们在自定义加载器中查找是否有需要加载的文件,如果已经加载过,直接返回字节码。
对应故事场景为:长老之子的下人自己解决不了,找到长老之子
2)如果自定义加载器没有加载过,则询问上一层加载器(即AppClassLoader)是否已经加载过。
对应故事场景为:长老之子携带宝物前去拜访解决此事
3) 如果没有加载过,则询问上一层加载器(ExtClassLoader)是否已经加载过。
对应故事场景为:大长老解决此事
4) 如果没有加载过,则继续询问上一层加载(BoopStrap ClassLoader)是否已经加载过
对应故事场景为:宗主解决此事
5) 如果BoopStrap ClassLoader依然没有加载过,则到自己指定类加载路径下("sun.boot.class.path")
查看是否有对应XXX.class字节码,有则返回,没有则通知下一层加载器ExtClassLoader到自己指定的
类加载路径下(java.ext.dirs)查看
6) 最后到自定义类加载器指定的路径还没有找到对应XXX.class字节码,则抛出异常ClassNotFoundException
双亲委派有什么好处呢?
1、比如两个类A和类B都要加载Integer类:
如果不用委托而是自己加载自己的,那么类A就会加载一份Integer字节码,然后类B又会加载一份Integer字节码,这样内存中就出现了两份Integer字节码。
如果使用委托机制,会递归的向父类查找,也就是首选用Bootstrap尝试加载,如果找不到再向下。这里的Integer就能在Bootstrap中找到然后加载,如果此时类B也要加载Integer,也从Bootstrap开始,此时Bootstrap发现已经加载过了Integer那么直接返回内存中的Integer而不需要重新加载,这样内存中就只有一份Integer的字节码了。
2、 安全性:
因为ClassLoader加载的class文件来源很多,比如编译器编译生成的class、其他工具生成的字节码。而有一些一些来源的class文件是不安全的,比如我们自定义一个java.lang.Integer类来覆盖jdk中默认的Integer类。里面写这么一句代码 System.exit(0);
初始化这个Integer的构造器是会退出JVM,破坏应用程序的正常进行,如果使用双亲委派机制的话该Integer类永远不会被调用,以为委托BootStrapClassLoader加载后会加载JDK中的Integer类而不会加载自定义的这个
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。