概述

ClassLoader的双亲托付模式:classloader 按级别分为三个级别:最上级 : bootstrap classLoader(根类载入器) ; 中间级:extension classLoader (扩展类载入器) 最低级 app classLoader(应用类载入器)。

根(Bootstrap)类载入器:该载入器没有父载入器。它负责载入虚拟机的核心类库,如java.lang.*等。比如java.lang.Object就是由根类载入器载入的。根类载入器从系统属性sun.boot.class.path所指定的文件夹中载入类库。

根类载入器的实现依赖于底层操作系统。属于虚拟机的实现的一部分,它并没有继承java.lang.ClassLoader类。

扩展(Extension)类载入器:它的父载入器为根类载入器。它从java.ext.dirs系统属性所指定的文件夹中载入类库,或者从JDK的安装文件夹的jre/lib/ext子文件夹(扩展文件夹)下载入类库,假设把用户创建的JAR文件放在这个文件夹下,也会自己主动由扩展类载入器载入。扩展类载入器是纯Java类,是java.lang.ClassLoader类的子类。

系统(System)类载入器:也称为应用类载入器。它的父载入器为扩展类载入器。

它从环境变量classpath或者系统属性java.class.path所指定的文件夹中载入类,它是用户自己定义的类载入器的默认父载入器。

系统类载入器是纯Java类。是java.lang.ClassLoader类的子类。

父子载入器并不是继承关系。也就是说子载入器不一定是继承了父载入器。

对于Java来说,java 虚拟机要将被用到的java类文件通过classLoader 载入到JVM内存中。而这个classloader就是bootstrap classloader。而对于APP而言,首先请求app 级来载入,继而请求extension classLoader,最后启动bootstrap classLoader 。

自己定义ClassLoader

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhbmd6aGlob25nOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

public class MyClassLoader extends ClassLoader {

    //类载入器名称
private String name;
//载入类的路径
private String path = "D:/";
private final String fileType = ".class";
public MyClassLoader(String name){
//让系统类载入器成为该 类载入器的父载入器
super();
this.name = name;
} public MyClassLoader(ClassLoader parent, String name){
//显示指定该类载入器的父载入器
super(parent);
this.name = name;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} @Override
public String toString() {
return this.name;
} private byte[] loaderClassData(String name){
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
this.name = this.name.replace(".", "/");
try {
is = new FileInputStream(new File(path + name + fileType));
int c = 0;
while(-1 != (c = is.read())){
baos.write(c);
}
data = baos.toByteArray(); } catch (Exception e) {
e.printStackTrace();
} finally{
try {
is.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
} @Override
public Class<? > findClass(String name){
byte[] data = loaderClassData(name);
return this.defineClass(name, data, 0, data.length);
} public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//loader1的父载入器为系统类载入器
MyClassLoader loader1 = new MyClassLoader("loader1");
loader1.setPath("D:/lib1/");
//loader2的父载入器为loader1
MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
loader2.setPath("D:/lib2/");
//loader3的父载入器为根类载入器
MyClassLoader loader3 = new MyClassLoader(null, "loader3");
loader3.setPath("D:/lib3/"); Class clazz = loader2.loadClass("Sample");
Object object = clazz.newInstance();
}
}
public class Sample { public Sample(){
System.out.println("Sample is loaded by " + this.getClass().getClassLoader());
new A();
}
}
public class A { public A(){
System.out.println("A is loaded by " + this.getClass().getClassLoader());
}
}

每个自己定义ClassLoader都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader。我们能够看一下ClassLoader这个抽象类中有一个getParent()方法,这种方法用来返回当前 ClassLoader的parent,注意,这个parent不是指的被继承的类,而是在实例化该ClassLoader时指定的一个 ClassLoader,假设这个parent为null,那么就默认该ClassLoader的parent是bootstrap classloade。

上面解说了一下ClassLoader的作用以及一个最主要的载入流程,接下来我们说说ClassLoader使用了双亲托付模式进行类载入。

ClassLoader

双亲托付模式

通俗的讲,就是某个特定的类载入器在接到载入类的请求时,首先将载入任务托付给父类载入器。依次递归。假设父类载入器能够完毕类载入任务,就成功返回;仅仅有父类载入器无法完毕此载入任务时,才自己去载入。

为了更好的理解双亲托付模式,我们先自己定义一个ClassLoader,假设我们使用这个自己定义的ClassLoader载入 java.lang.String,那么这里String是否会被这个ClassLoader载入呢?

其实java.lang.String这个类并不会被我们自己定义的classloader载入。而是由bootstrap classloader进行载入,为什么会这样?实际上这就是双亲托付模式的原因,由于在不论什么一个自己定义ClassLoader载入一个类之前,它都会先 托付它的父亲ClassLoader进行载入,仅仅有当父亲ClassLoader无法载入成功后,才会由自己载入。

而在上面的样例中,由于 java.lang.String是属于java核心API的一个类,所以当使用自己定义的classloader载入它的时候。该 ClassLoader会先托付它的父亲ClassLoader进行载入(bootstrap classloader),所以并不会被我们自己定义的ClassLoader载入。

我们来看一下ClassLoader的一段源代码:

protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException{
// 首先检查该name指定的class是否有被载入
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//假设parent不为null,则调用parent的loadClass进行载入
c = parent.loadClass(name, false);
}else{
//parent为null,则调用BootstrapClassLoader进行载入
c = findBootstrapClass0(name);
}
}catch(ClassNotFoundException e) {
//假设仍然无法载入成功,则调用自身的findClass进行载入
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}

使用双亲托付模式长处

那么我们使用双亲托付模式有什么长处呢?

  1. 由于这样能够避免反复载入。当父亲已经载入了该类的时候,就没有必要子ClassLoader再载入一次。

  2. 考虑到安全因素,我们试想一下,假设不使用这样的托付模式,那我们就能够随时使用自己定义的String来动态替代java核心api中定义类型,这样会存在很大的安全隐患。而双亲托付的方式,就能够避免这样的情况,由于String已经在启动时被载入,所以用户自己定义类是无法载入一个自己定义的ClassLoader。

附:Android ClassLoader简单介绍

android classloader双亲托付模式的更多相关文章

  1. android深入之设计模式(一)托付模式

    (一)托付模式简单介绍 托付模式是主要的设计模式之中的一个.托付.即是让还有一个对象帮你做事情. 更多的模式,如状态模式.策略模式.訪问者模式本质上是在更特殊的场合採用了托付模式. 托付模式使得我们能 ...

  2. classloader加载的双亲委托模式

    要深入了解ClassLoader,首先就要知道ClassLoader是用来干什么的,顾名思义,它就是用来加载Class文件到JVM,以供程序使用 的.我们知道,java程序可以动态加载类定义,而这个动 ...

  3. jdbc 加载数据库驱动如何破坏双亲委托模式

    导读      通过jdbc链接数据库,是每个学习Java web 方向的人必然一开始会写的代码,虽然现在各路框架都帮大家封装好了jdbc,但是研究一下jdbc链接的套路还是很意义     术语以及相 ...

  4. java安全沙箱(一)之ClassLoader双亲委派机制

    java是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件检验器 内置于Java虚拟机(及语言)的安全特性 安全管理器及J ...

  5. Android Immersive Mode (沉浸模式) 还是 Translucent Bars (透明状态栏)

    Immersive Mode (沉浸模式) 还是 Translucent Bars (透明状态栏) [科普]什么叫真正的“沉浸式”状态栏? 为什么在国内会有很多用户把「透明栏」(Translucent ...

  6. 【转】Android 当打开“开发者模式”中的“不保留活动”后,程序应当怎么保持正常运行

    当打开这个设置以后,程序的Activity会自动销毁,每次返回的时候就会不断重oncreate,此时伴随的问题多多. 参考文档:http://www.bubuko.com/infodetail-960 ...

  7. android Activity的启动模式

    Android中Activity启动模式详解   在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启 ...

  8. Android设计模式系列-组合模式

    Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用.在android UI设计,几乎所有的widget和布局类都依靠这两个类.组合模式,Compos ...

  9. cocos2d-x 托付模式的巧妙运用——附源代码(一)

    先来说一下托付模式是什么.以下的内容摘要自维基百科: 托付模式是软件设计模式中的一项基本技巧.在托付模式中,有两个对象參与处理同一个请求.接受请求的对象将请求托付给还有一个对象来处理.托付模式是一项基 ...

随机推荐

  1. 【laravel5.4】DB::table的操作

    基于laravel5.4版本的查询构造器的简单几个操作:(相对于TP3.2版本) //获取指定多行多列,二维,,对象 [select] $names = ''; $names = DB::table( ...

  2. C-常用构造哈希函数

    1.定址法(比如0-100岁的人数统计, 可以按年龄作为散列地址, 1980年后每年出生人数的统计, 可以把"年限 - 1980"作为散列地址) 2.取余法 3.数字分析法(比如一 ...

  3. Sphinx高亮显示关键字

    选取程序中使用的一部分代码: public function buildExcerptRows($ids) { $options = array( 'before_match' => '< ...

  4. Python3.2官方文档翻译--输出格式化

    第八章 标准库二 第二部分涵盖了很多更能满足专业开发者需求的高级模块.这些模块在小脚本中非常少出现. 8.1 输出格式化 Reprlib模块为大型的或深度嵌套的容器缩写显示提供了repr()函数的一个 ...

  5. C++二维数组讲解、二维数组的声明和初始化

    我们知道,一维空间是一条线,数学中用一条数轴来表达:二维空间是一个平面,数学中用平面坐标系来表达.那么二维数组又是什么样的呢? 线与面 我们用一个下标来描述一维数组中的某个元素,就好像在用数描述一条线 ...

  6. B/S打印解决方案参考

    使用Lodop 插件,该插件占用8000端口,未使用过,仅知依赖浏览器打印 http://blog.csdn.net/harderxin/article/details/17262945 强大的web ...

  7. 如何让maven 将工程依赖的jar 复制到WEB-INF/lib 目录下

    1.在默认生命周期的compile阶段执行 maven-dependency-plugin:copy-dependencies命令即可:<plugin><groupId>org ...

  8. atitit.spring hibernate的事务机制 spring不能保存对象的解决

    atitit.spring hibernate的事务机制 spring不能保存对象的解决 sessionFactory.openSession() 不能..log黑头马sql语言.. sessionF ...

  9. [svc]nfs客户端报错解决Stale file handle

    NFS故障: 问题背景: 客户端挂载是好的.服务端磁盘满了,重新给挂了一快.客户端df -h 发现nfs挂载消失. 查看目录客户端报错:Stale file handle 现象如下: [root@n1 ...

  10. [na]tcp的可靠性