类的加载和双亲委派模型

类加载器基本概念

顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。

任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性。

类加载器类型

  • 启动类(引导类)加载器 Bootstrap ClassLoader, 虚拟机的一部分,由c++实现。负责加载<JAVA_HOME>/lib下的类库
  • 扩展类加载器 Extension ClassLoader, sun.misc.Launcher$ExtClassLoader.负载加载<JAVA_HOME>/lib/ext下的类库
  • 应用程序类加载器 Application ClassLoader ,sun.misc.Launcher$AppClassLoader, 它是System.getClassLoader()的返回值,也称为系统类加载器。负责加载用户类路径上所指定的类库。如果应用程序没有自定义过类加载器,一般它就是默认的类加载器。Thread.currentThread.getContextClassLoader,如果没有setContextClassLoader,默认也是它。

双亲委派模型

双亲委派模型的过程:如果一个类加载器收到了类加载的请求,首先不会自己去加载,而是把请求为派给自己的父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父类反馈自己无法完成这个加载请求时,子类加载器才会尝试自己完成。

好处就是:Object类在程序的各种类加载器环境中都是同一个类。不会造成混乱。

破坏双亲委派模型

双亲委派模型只是推荐,而非强制。有三次大规模破坏该模型的情况。

  1. JDK 1.0->1.2 , loadClass() -> findClass()
  2. 模型缺陷:JNDI服务,SPI扩展类是由厂商自己实现,而启动类加载又不可能认识这些类。只好引入 线程上下文 类加载器Thread Context ClassLoader. 该类加载器可以通过setContextClassLoader()设置,如果创建线程时未设置,将会从父线程继承。如果在应用的全局范围内都没有设置,那就默认是AppClassLoader.  有了这个,JNDI服务就可以去加载所需的SPI扩展代码,也就是父类加载器请求子类加载器去完成类加载的动作。这其实也就违背了双亲委派模型的一般性原则,但无可奈何。
  3. 程序动态性的追求: “热替换” , OSGI. JSR-291

开发自己的类加载器

 

继承 java.lang.ClassLoader,覆盖findClass(String name)即可。

java.lang.ClassLoader类的方法 loadClass()封装了前面提到的代理模式的实现。该方法会首先调用 findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass()方法,而是覆写 findClass()方法。

示例:

public class FileSystemClassLoader extends ClassLoader { 

    private String rootDir; 

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    } 

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        else {
            return defineClass(name, classData, 0, classData.length);
        }
    } 

    private byte[] getClassData(String className) {
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    } 

    private String classNameToPath(String className) {
        return rootDir + File.separatorChar
                + className.replace(‘.‘, File.separatorChar) + ".class";
    }
 }

defineClass()方法负责把字节码转为java.lang.Class类的实例。

时间: 01-15

类的加载和双亲委派模型的相关文章

《深入理解Java虚拟机》笔记 第七章 虚拟机加载机制及双亲委派模型

? ? 站在虚拟机的角度上,只存在两种不同的类加载器: ? ? 一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分: ? ? 另外一种就是其它所有的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader. ? 从Java开发人员的角度看,类加载器还可以划分得更细一些,如下: ? ? 1.启动类加载器(Bootstrap ClassLoader):这个类加载器负责将放

双亲委派模型

双亲委派模型的概念 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因为所有的类请求都会传给顶层的启动类加载器(Bootstrap ClassLoader),只有当父加载器反馈自己无法完成该加载请求时,子加载器才会尝试自己去加载. 双亲委派模型机制图 加载类 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中.由于引导类

类加载机制,双亲委派模型

类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型. 类加载时机:类的生命周期是从类被加载到虚拟机内存中,到卸载内存为止. 类的生命周期:加载 loading ---> 连接(验证 verification 准备 preparation 解析 resolution) ---> 初始化 initialization ---> 使用 using --- > 卸载 unloading 类加载过程  

Java双亲委派模型

Java双亲委派模型详解 我们在了解双亲委派模型之前,不得不先了解一下什么是类加载器.虚拟机设计团队之初是希望类加载过程"通过一个类的全限定名来获取描述该类的二进制字节流"这个动作能放到虚拟机外部实现,以便于让程序自己决定如何获取该类,实现这个动作的代码的工具成为类加载器. 可能很多人觉得类加载器,顾名思义,就是个加载类的嘛,有啥大不了的,但是类加载这个过程是很严格的,对于任意一个类,我们都需要由加载他的类加载器和类的本身来决定该类在虚拟机之中的唯一性.什么意思呢??就是说我们的虚拟机

双亲委派模型,类的加载机制,搞定大厂高频面试题

看过这篇文章,大厂面试你「双亲委派模型」,硬气的说一句,你怕啥? 读该文章姿势 打开手头的 IDE,按照文章内容及思路进行代码跟踪与思考 手头没有 IDE,先收藏,回头看 (万一哪次面试问了呢) 需要查看和拷贝代码,点击文章末尾出「阅读原文」 文章内容相对较长,所以添加了目录,如果你希望对 Java 的类加载过程有个更深入的了解,同时增加自己的面试技能点,请耐心读完...... 双亲委派模型 在介绍这个Java技术点之前,先试着思考以下几个问题: 为什么我们不能定义同名的 String 的 ja

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

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

【转】Android类动态加载技术

http://www.blogjava.net/zh-weir/archive/2011/10/29/362294.html Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的普通需求.但是有些特殊问题,常常引发我们进一步的沉思.我们从沉思中产生顿悟,从而产生新的技术形式. 如何开发一个可以自定义控件的Android应用?就像eclipse一样,可以动态加载插件:如何让Android应用执行服务器上的不可预知的代码?如何对Android应用加密,而只在执行时自解密,从而防

jvm类加载器和双亲委派模型

类加载器按照层次,从顶层到底层,分为以下三种: (1)启动类加载器(Bootstrap ClassLoader) 这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中.启动类加载器无法被Java程序直接引用. (2)扩展类加载器(Extension ClassLoader) 这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有

Java温故而知新(10)类的加载机制

类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行. 研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性. 1.类加载机制 我们来了解一下虚拟机如何加载Class文件. 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被直接使用的java类型,这就是虚拟机的类加载机制. 类的生命周期包括加载(Loading).验证(Verification).准