Java教程——类加载器

开课吧开课吧锤锤2021-06-23 11:50

    前面已经讲过很多类的声明周期,更多的是一种理论基础,它映射到特定的代码层,用来完成类装入过程的确切方式就是这里要说的——类装入器。

Java

    虚拟机当设计时,将类加载阶段中的“通过一个类的全路径名获得该字节码二进制流”操作放在Java虚拟机外部完成,负责执行该操作的模块称为类加载器。

    类加载器分类

Java

    启动类加载器

    1、它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar,sun.boot.class.path路径下的内容),并不是Java代码完成,而是用原生代码(C语言或C++)来实现的,并不继承自java.lang.ClassLoader。

    2、加载扩展类和应用程序类加载器。并指定他们的父类加载器。

    扩展类加载器

    这一类加载器由sun.misc.Launcher$ExtClassLoader实现,用来加载Java的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容)。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。

    应用类加载器

    这个类加载器由sun.misc.Launcher$AppClassLoader实现,由于这个类加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以它也成为系统类加载器。它负责加载用户类路径下所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是系统默认的类加载器。

    自定义加载器

    开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

    类加载的代理(双亲委派模式)

Java

    如果一个类加载器收到了类加载器的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父加载器去完成。每个层次的类加载器都是如此。因此所有的加载请求最终都会传送到Bootstrap类加载器(启动类加载器)中,只有父类加载反馈自己无法加载这个请求(它的搜索范围中没有找到所需的类)时子加载器才会尝试自己去加载。

    例如类java.lang.Object,它存放在rt.jart之中,无论哪一个类加载器都要加载这个类.最终都是双亲委派模型最顶端的Bootstrap类加载器去加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型.由各个类加载器自行去加载的话,如果用户编写了一个称为“java.lang.Object”的类,并存放在程序的ClassPath中,那系统中将会出现多个不同的Object类.java类型体系中最基础的行为也就无法保证。应用程序也将会一片混乱。

    当然也并不是所有的加载机制都是双亲委派的方式,例如tomcat作为一个web服务器,它本身实现了类加载,该类加载器也使用代理模式(不同于前面说的双亲委托机制),所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。但也是为了保证安全,这样核心库就不在查询范围之内。

    类的加载时机

    最后说一个比较重要也是诸多困惑的地方,就是什么时候才会加载类。

    加载、验证、准备、初始化、卸载这五个步骤是确定的,类的加载过程必须按部就班地开始,但是解析阶段就不一定了,它在某些情况下是可以在初始化阶段之后再开始,看到这里,肯定满脑子????,其实不必惊讶,我一开始就说了,它这是为了满足Java语言地动态时绑定(泛型、多态的本质)这个特性来的,它是按部就班的开始,而不是按部就班的“进行”或者“结束”,这些阶段其实是相互交叉混合进行的,通常会在一个阶段执行的过程中调用、激活另外一个阶段。

    其实上面的话有些绕,我们从类的使用上来看这个问题,类的使用分为主动引用和被动引用:

    1、主动引用类(肯定会初始化)

    new一个类的对象。

    调用类的静态成员(除了final常量)和静态方法。

    使用java.lang.reflect包的方法对类进行反射调用。

    当虚拟机启动,javaHello,则一定会初始化Hello类。说白了就是先启动main方法所在的类。

    当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类

    被动引用

    当访问一个静态域时,只有真正声明这个域的类才会被初始化。例如:通过子类引用父类的静态变量,不会导致子类初始化。

    通过数组定义类引用,不会触发此类的初始化。

    引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)。

    首先,Java的编译不是像其他语言一样,都加载到内存中才开始运行,而且动态的,也就会出现:先运行了一部分,初始化了一些类,但是在这一部分运行的代码里被动引用了未被初始化的类(例如static变量),这时候就会出现了这种违背顺序的情况。总的来说就是,

    先加载并连接当前类

    父类没有被加载,则去加载、连接、初始化父类,依旧是先加载并连接,然后再判断有无父类,如此循环(所以JVM先将Object加载)

    如果类中有初始化语句,包括声明时赋值与静态初始化块,则按顺序进行初始化

    以上就是开课吧广场小编为大家整理的“Java教程——类加载器”一文,更多相关信息尽在开课吧广场Java教程频道。

免责声明:本站所提供的内容均来源于网友提供或网络搜集,由本站编辑整理,仅供个人研究、交流学习使用。如涉及版权问题,请联系本站管理员予以更改或删除。
有用
分享
全部评论快来秀出你的观点
登录 后可发表观点…
发表
暂无评论,快来抢沙发!
高并发编程训练营