深入理解ClassLoader(四)—类的父委托加载机制

上几次我们介绍到了JVM内部的几个类加载器,我们来重新画一下这个图,再来看一下他们之间的关系。

JVM的ClassLoader采用的是树形结构,除了BootstrapClassLoader以外?每个ClassLoader都会有一个parentClassLoader,用户自定义的ClassLoader默认的parentClassLoader是SystemClassLoader,当然你可以自己指定需要用哪一个ClassLoader的实例,我们来看他的API

默认的无参构造方法使用的是SystemClassLoader,你可以通过传入一个ClassLoader的实例来指定他的父类加载器。这里强调一点,很多人认为各个父子类加载器之间是继承关系,这里澄清一下,父子类加载器之间是组合关系,子类类加载器会含有一个parentClassLoader的对象,类加载的时候通常会按照树形结构的原则来进行,也就是说,首先是从parentClassLoader中尝试进行加载,当parent无法进行加载时,再从当前的类加载器进行加载,以此类推。JVM会保证一个类在同一个ClassLoader中只会被加载一次。

ClassLoader抽象类为我们定义了一系列的关键的方法,下来让我们来看一下

1、  loadClass方法,此方法用来加载指定名字的类,ClassLoader会先从已加载的类中寻找,如果没有,则使用父加载器进行加载,如果加载成功则加载,否则从当前的类加载器中进行加载,如果还没有找到该类的class文件则会抛出异常ClassNotFoundException

如果该类需要链接,则通过resolveClass进行链接。

2、  defineClass,此方法用来将二进制的字节码转换为Class对象,这个对类的自定义加载非常重要,当然前文我们已经说了,当类的二进制文件被加载到内存之后,要进行语法分析,语义分析等一系列的验证,如果不符合JVM规范,则抛出ClassFormateError错误,如果生成的类名和字节码中的不一致,则抛出NoClassDefFoundException,如果加载的class是受保护的、采用不同的标签名的,或者一java.*开头的,则抛出SecurityException,如果要加载的class在之前已经被加载过,则直接抛出LinkageError。

3、resolveClass,此方法完成Class的链接,如果链接过则直接返回。当Java开发人员调用Class.forName来获取一个class对象的时候,JVM会从方法栈上寻找第一个ClassLoader,通常也就是执行Class.forName的ClassLoader,并使用这个ClassLoader来加载此类。JVM为了保护加载、执行的类的安全,不允许ClassLoader直接卸载加载了的类,只有JVM才可以卸载,在SUN的JDK中,只有ClassLoader没有 被引用的时候,次ClassLoader加载的类才会被卸载!

附:JDK中ClassLoader的部分源码

1、 构造函数

protected ClassLoader(ClassLoader parent) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkCreateClassLoader();
    }
    this.parent = parent;
    initialized = true;
}
    protected ClassLoader() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkCreateClassLoader();
    }
    this.parent = getSystemClassLoader();
    initialized = true;
    }

2loadClass

 public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
    }
    protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
       if (parent != null) {
           c = parent.loadClass(name, false);
       } else {
           c = findBootstrapClass0(name);
       }
        } catch (ClassNotFoundException e) {
            // If still not found, then invoke findClass in order
            // to find the class.
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

类的这种加载机制我们称之为父委托加载机制,父委托机制的优点就是能够提高软件系统的安全性。因为在词机制下,用户自定义的类加载器不可能加载本应该由父加载器加载的可靠类,从而防止不可靠的恶意代码代替由父类加载器加载的可靠类,从而防止不可靠的甚至恶意的代码代替由父类加载器加载的可靠代码。如,java.lang.Object类总是由根类加载器加载的,其他任何用户自定义的类加载器都不可能加载含有恶意代码的java.lang.Object类。

被定义的类加载器,而它的父类加载器则被称为初始类加载器。

我们知道java中很可能出现类名相同的类,但是JVM却能正常的加载,是因为我们将相同的类名的类放在了不通的包(package)下面,这个也成为命名空间,每个类加载器都有自己的命名空间,命名空间是由该加载器以及所有父加载器所加载的类组成。在同一个命名空间中,不会出现类的完整名字(包名+类名)相同的两个类;在不同的命名空间中,有可能出现类的完整名字相同的两个类。

由同一类加载器加载的属于相同包的类组成了运行时包。决定两个类是不是属于同一个运行时包,不仅要看他们的包名称是否相同,还要看定义类加载器是否相同。只有属于同一运行时包的类之间才能相互访问可见(默认访问级别)的类和成员。假设用户自定义了一个类java.lang.TestCase并由用于自定义的类加载器加载,由于java.lang.TestCase和核心类库java.lang.*由不同的类加载器加载,他们属于不同的运行时包,所以java.lang.TestCase不能访问核心库java.lang包中的包可见成员。

同一个命名空间内的类是相互可见的。

子类加载器的命名空间包含所有父类加载器的命名空间,因此由子类加载器加载的类能看见父类加载器加载的类,相反,由父类加载器加载的类不能看见子类加载器加载的类。如果两个加载器之间没有直接或者间接的父子关系,那么他们各自加载的类互不可见。

时间: 2024-09-28 18:32:27

深入理解ClassLoader(四)—类的父委托加载机制的相关文章

20150112--抽象类+接口+重载+自动加载机制-02

重载 重载:overload,被重新加载.在面向对象里,重载指的当一个函数(方法),具有不同的返回值或者参数列表的时候,可以有不同的存在形式(允许同名函数存在:返回值或者参数列表不一样) class Person{ public string function eat(){//吃饭方法;return '吃饱了';} public string function eat(水果){//吃水果;return '好吃';} public string function eat(主食){//吃主食;ret

20150112--抽象类+接口+重载+自动加载机制-01

回顾 面向对象:自动加载(__autoload),类常量访问(范围解析操作符),静态(属性和方法:self) 设计模式:单例模式(三私一公),工厂模式 面向对象三大特性:封装,继承和多态(PHP不支持) PHP继承:extends(对象继承:属性和非私有方法) PHP继承 方法重写 子类拥有与父类同名的方法. 除了私有的属性和方法之外,系统一律都是去子类中查找属性和方法,但是如果对应的属性和方法是私有的,那么系统一定要去判断$this所代表的上下文(类环境),在哪个类就访问哪个类的私有属性和方法

Java重要技术(28)类加载器之类加载器的层次关系和委托加载机制

1.1. 类加载器的层次 类加载器包括三种: Bootstrap ClassLoader:用于加载JRE的lib目录下的jar文件中的class. ExtClassLoader:用于加载JRE的lib/ext目录下的jar文件中的class. AppClassLoader:用于加载classpath下的class. 在加载一个class时,通常应该先委托给parent类加载器来加载,parent类加载器找不到这个类时,才自行加载.实际优先次序从高到低排列是BootStrap ClassLoade

类的加载机制(四)

这一章我们主要是对双亲委派机制进行详细讲解: 前面我们知道类加载有系统自带的3种加载器,也有自定义的加载器,那么这些加载器之间的关系是什么,已经在加载类的时候,谁去加载呢?这节,我们将进行讲解. 一.双亲委派机制 JVM的ClassLoader采用的是树形结构,除了BootstrapClassLoader以外?每个ClassLoader都会有一个parentClassLoader,用户自定义的ClassLoader默认的parentClassLoader是SystemClassLoader,当然

访问修饰限定符的简单总结、final/abstruct/interface对类的限制、自动加载机制、序列化与反序列化【数据持久化和对象的序列化问题】、对象的拷贝(按引用是因为对象标识)和克隆(__clone方法中的this指向)

1.针对访问修饰限定符的理解只需要两点:(1)针对的是类的概念和访问代码的位置来确定是否能够访问(2)对访问修饰限定符的使用时只需要对该成员的使用场景注意即可[也就是内部,继承类,外部进行访问的权限] 不需要对内部进行太多理解[需要对php底层理解时进行理解] [重点][用途]通过访问修饰限定符将内部成员的权限合理的限制,然后再使用公共接口来调用这个基本服务,保证外部不能访问其内部的构件[这样既能够通过类内的设置,将内部的功能实现更好的限制,只有最外层的接口可以正常被访问到,而不了解内部的业务]

Java ClassLoader加载机制理解 实际例子

针对 Java ClassLoader加载机制理解, 做了个如何自定制简单的ClassLoader,并成功加载指定的类. 不废话,直接上代码. package com.chq.study.cl; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOExcept

PHP中的use、命名空间、引入类文件、自动加载类的理解

use只是使用了命名空间,但是要想调用类,必须要加载类文件,或者自动加载. 即便是引入了其中一个类,如果没有自动加载机制,还是会报错 use的几种用法 namespace Blog\Article; class Comment { } //创建一个BBS空间(我有打算开个论坛) namespace BBS; //导入一个命名空间 use Blog\Article; //导入命名空间后可使用限定名称调用元素 $article_comment = new Article\Comment(); //为

jvm系列(一):java类的加载机制

java类的加载机制 原文:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载器并不需要等到某个

Java ClassLoader加载机制

一.体系结构(自上向下) 1.Bootstrap ClassLoader(BootStrapClassLoader) --- 启动类加载器或者叫引导类加载器,加载jdk核心的APIs,这些APIs一般位于jdk_home/lib下:它是一个本地接口,所以不能从java代码中得到它的信息.例如, log(java.lang.String.class.getClassLoader())得到的是null. 2.Extension ClassLoader(Launcher$ExtClassLoader)