Java语言 类加载器(ClassLoader)/双亲委派模型代码实例讲解
小标 2018-12-04 来源 : 阅读 755 评论 0

摘要:本文主要向大家介绍了Java语言 类加载器(ClassLoader)/双亲委派模型代码实例讲解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了Java语言 类加载器(ClassLoader)/双亲委派模型代码实例讲解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。


ClassLoader类加载器


Class类描述的是整个类的信息,在Class类中提供的forName()方法,这个方法根据ClassPath配置的路径进行类的加载,如果说现在你的类的加载路径可能是网络、文件,这个时候就必须实现类加载器,也就是ClassLoader类的主要作用。


认识ClassLoader


首先通过Class类观察如下方法:


//自定义类,这个类一定在CLASSPATH中

class Member{}

 

public class Test {

    public static void main(String[] args) {

        Class cls = Member.class ;

        System.out.println(cls.getClassLoader()) ;

        System.out.println(cls.getClassLoader().getParent()) ;

        System.out.println(cls.getClassLoader().getParent().getParent());

    }

}

   


运行结果



此时出现了两个类加载器:ExtClassLoader(扩展类加载器)、AppClassLoader(应用程序类加载器)。


那么,什么是类加载器?



Bootstrap(启动类加载器):这个类加载器使用C++实现,是虚拟机自身的一部分;其他的类加载器都由Java语言实现,独立于JVM外部并且都继承于java.lang.ClassLoader.BootStrap类加载器负责将存放于\lib目录中(或者被-Xbootclasspath参数指定路径中)能被虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到JVM内存中。启动类加载器无法被Java程序直接引用。


ExtClassLoader(扩展类加载器):它负责加载\lib\ext目录中,或者被java.ext.dirs系统变量指定的路径中的类库。开发者可以直接使用扩展类加载器。


AppClassLoader(应用程序类加载器):负责加载用户类路径(ClassPath)上指定的类库,如果应用程序中没有自定义自己的类加载器,则此加载器就是程序中默认的类加载器。


双亲委派模型


我们的应用程序都是由这三种加载器互相配合进行加载的,如果有必要,还可以加入自定义的类加载器。这些类加载器的关系一般如下图所示:



上图展示的类加载器之间的这种层次关系,就称为类加载器的双亲委派模型。双亲委派模型要求除了顶层的父类加载器外,其余的类加载器都应有自己的父类加载器。


双亲委派模型的工作流程是:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此。因此,所有的加载请求都应当传送到顶层的BootStrap加载器中,只有当父加载器反馈无法完成这个加载请求时(在自己搜索范围中没有找到此类),子加载器才会尝试自己去加载。


类加载器的双亲委派模型从JDK1.2引入后被广泛应用于之后几乎所有的Java程序中,但它并不是强制性约束,甚至可以破坏双亲委派模型来进行类加载,最典型的就是OSGI技术。


例:观察CLassLoader.loadClass()方法


// First, check if the class has already been loaded

    Class c = findLoadedClass(name);

    if (c == null) {

        long t0 = System.nanoTime();

        try {

            if (parent != null) {

                c = parent.loadClass(name, false);

            } else {

                c = findBootstrapClassOrNull(name);

            }

        } catch (ClassNotFoundException e) {

            // ClassNotFoundException thrown if class not found

            // from the non-null parent class loader

        }

        if (c == null) {

            // If still not found, then invoke findClass in order

            // to find the class.

            long t1 = System.nanoTime();

            c = findClass(name);

        }

    }

    if (resolve) {

        resolveClass(c);

    }

   


* 自定义类加载器*


自定义类加载器:用户决定类从哪里加载。


ClassLoader类中提供有如下方法(进行类的加载):


ClassLoader类中提供有如下方法(进行类的加载):

protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException

   


例:观察默认类加载器


// 自定义类,这个类一定在CLASSPATH中

class Member{

    @Override

    public String toString() {

        return "Member";

    }

}

public class TestDemo {

    public static void main(String[] args) throws Exception{

        System.out.println(Class.forName("Member").getClassLoader().loadClass("Member").newInstance());

    }

}

   


例:在Desktop上建立Member.java文件


// 自定义类,这个类一定在CLASSPATH中

class Member{

    @Override

    public String toString() {

        return "Member";

    }

}

   


随后将此文件用javac编译后生成class文件。现在希望通过自定义的类加载器实现/Desktop/Member.class文件的加载。


ClassLoader提供的类加载:

protected final Class defineClass(String name, byte[] b, int off, int len) throws ClassFormatError


import java.io.ByteArrayOutputStream;

import java.io.FileInputStream;

import java.io.InputStream;

// 自定义类加载器

class MyClassLoader extends ClassLoader {

    /**

    * 实现一个自定义的类加载器,传入类名称,通过指定路径加载

    * @param className 类名称

    * @return 返回的Class对象

    * @throws Exception

    */

    public Class loadData(String className) throws Exception {

        // 加载类文件的信息

        byte[] classData = this.loadClassData() ;

        return super.defineClass(className,classData,0,classData.length) ;

    }

    /**

    * 通过指定的文件路径进行类的文件加载,实际上就是进行二进制文件读取

    * @return 类文件数据

    * @throws Exception

    */

    private byte[] loadClassData() throws Exception {

        InputStream input = new FileInputStream("/Users/yuisama/Desktop/Member.class") ;

        // 取得所有字节内容,放到内存中

        ByteArrayOutputStream bos = new ByteArrayOutputStream() ;

        // 读取缓冲区

        byte[] data = new byte[20] ;

        int temp = 0 ;

        while ((temp = input.read(data))!=-1){

            bos.write(data,0,temp) ;

        }

        byte[] result = bos.toByteArray() ;

        input.close() ;

        bos.close() ;

        return result ;

    }

}

public class TestDemo {

    public static void main(String[] args) throws Exception{

        Class cls = new MyClassLoader().loadData("Member") ;

        System.out.println(cls.getClassLoader()) ;

        System.out.println(cls.getClassLoader().getParent()) ;

        System.out.println(cls.getClassLoader().getParent().getParent()) ;

        System.out.println(cls.newInstance());

    }

}

   


类加载器给用户提供最大的帮助为:可以通过动态的路径进行类的加载操作


比较两个类相等的前提:必须是由同一个类加载器加载的前提下才有意义。否则,即使两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那么这两个类注定不想等。


          

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程