java类jvm加载类的顺序顺序

关于jvm类jvm加载类的顺序器我们面试嘚时候会经常问到如果相关概念不清楚的话可以参阅我的 。这一篇文章旨在实战演示jvm执行的顺序

* 类jvm加载类的顺序器jvm加载类的顺序顺序栲究
 1.父类静态代码块:赋值b成功
 1.父类静态代码块:a的值1
 2.子类静态代码块:赋值sb成功
 2.子类静态代码块:sa的值1

当我们进行new实例化进行操作的时候,JVM都进行了什么操作呢 
如果你看过深入理解jvm的话,应该会比较清楚当我们使用new关键字进行实例化的时候,会进行如下几步处理:

此階段jvm加载类的顺序类的字节码信息到内存

此阶段进行验证分配默认值,符号引用转直接引用

对于我们的实例而言我们只关注连接和初始化阶段即可。 
连接阶段我们按照类结构(成员变量(先)方法(后)等)的顺序(不是书写顺序),先对变量进行赋默认值0对象的话为null。

初始化嘚时候就是对对象进行赋值,比如c执行initc()进行赋值

JVM把class文件jvm加载类的顺序到内存并對数据进行校验,解析和初始化最终形成JVM可以直接使用的Java类型的过程。

    1. 将class文件字节码内容jvm加载类的顺序到内存中并将这些静态数据转換成方法区中的运行时数据结构,在堆中生成一个代表这个类文件的java.lang.Class对象作为方法区类数据的访问接口。-----这个过程需要类jvm加载类的顺序器参与
    1. 验证:确保jvm加载类的顺序的类信息符合JVM规范没有安全方面的问题
    2. 准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
    3. 解析:虚拟机常量池内的符号引用替换为直接引用的过程
    1. 初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中所有变量的赋值动作和静态语句块中的语句合并产生的
    2. 当初始化一个类的时候,如果发现其父類还没有进行初始化则需要先将其父类进行初始化。
    3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中能被正确的加锁和同步
    4. 当访问一个java类嘚静态域时,只有真正声明这个域的类才会被初始化

jvm执行类jvm加载类的顺序的顺序

  1. 首先在一个类中找到主函数。主函数中实例化了一个类
  2. 這时候首先执行这个类中的静态代码块
  3. 然后再执行类中的其他方法
静态变量与静态域其实是一回事

Java语言的哲学:一切都是对象对於Java虚拟机而言,一个普通的Java类同样是一个对象那如果是对象,必然有它的初始化过程一个类在JVM中被实例化成一个对象,需要经历三个過程:jvm加载类的顺序、链接和初始化

  jvm加载类的顺序:从字节码二进制文件——.class文件将类jvm加载类的顺序到内存,从而达到类的从硬盘仩到内存上的一个迁移所有的程序必须jvm加载类的顺序到内存才能工作。一个Java类在被jvm加载类的顺序到内存后会在Java堆中创建一个类(java.lang.Class)对象同时JVM为每个类对象都维护一个常量池(类似于符号表)。

  • Bootstrap ClassLoader:这个jvm加载类的顺序器不是一个Java类而是由底层的c++实现,负责在虚拟机启动时jvm加載类的顺序Jdk核心类库以及jvm加载类的顺序后两个类jvm加载类的顺序器

  除了以上3个类jvm加载类的顺序其之外,用户还可以继承ClassLoader类来自定义相應的类jvm加载类的顺序器JVM通过一种双亲委托模型来避免重复jvm加载类的顺序同一个类,在这种模型中当一个类C需要被某一个类jvm加载类的顺序器Ljvm加载类的顺序时,会优先在类jvm加载类的顺序器L的父类中查找类C是否已经被jvm加载类的顺序下面的代码是具体的双亲委托模式的实现:

所有的Java类jvm加载类的顺序器都保证是引导类jvm加载类的顺序器的孩子,具体的ClassLoader体系结构见下图:

  链接:包含了验证和准备类或者接口、包括了它的直接父类、直接父接口、元素类型以及一些必要的操作Java虚拟机规范并没明确要求被准备的类或接口需要被解析,只需要验证相關的类或接口的字节码符合JVM规范

  类的初始化:执行类的static块和初始化类内部的静态属性。

  通常用这两种方式来动态jvm加载类的顺序┅个java类但是两个方法之间也是有一些细微的差别。

   查看Class类的具体实现可知实质上这个方法是调用原生的方法:

  •  保证一个Java类被有效得jvm加载类的顺序到内存中;
  •  类默认会被初始化,即执行内部的静态块代码以及保证静态属性被初始化;
  • 默认会使用当前的类jvm加载类的顺序器来jvm加载类的顺序对应的类

  如果采用这种方式的类jvm加载类的顺序策略,由于双亲托管模型的存在最终都会将类的jvm加载类的顺序任务交付给Bootstrap ClassLoader进行jvm加载类的顺序。跟踪源代码最终会调用原生方法:

  •  类不会被初始化,只有在之后被第一次调用时类才会被初始化;
  •  之所鉯采用这种方式的类jvm加载类的顺序是提供一种灵活度,可以根据自身的需求继承ClassLoader类实现一个自定义的类jvm加载类的顺序器实现类的jvm加载类嘚顺序(很多开源Web项目中都有这种情况,比如tomcatstruct2,jboss原因是根据Java Servlet规范的要求,既要Web应用自己的类的优先级要高于Web容器提供的类但同时叒要保证Java的核心类不被任意覆盖,此时重写一个类jvm加载类的顺序器就很必要了)

         对于普通的Java程序一般都不需要显式的声明来动态jvm加载类嘚顺序Java类,只需要用import关键字将相关联的类引入类被第一次调用的时候,就会被jvm加载类的顺序初始化那对于一个类对象,其内部各组成蔀分的初始化顺序又是如何的呢?

         一个Java类对象在初始化的时候必定是按照一定顺序初始化其静态块、静态属性、类内部属性、构造方法这裏我们讨论的初始化分别针对两个对象,一个是类本身还有一个是类实例化的对象

类本身的初始化会在类被jvm加载类的顺序完毕、链接完荿之后,由Java虚拟机负责调用<clinit>方法完成在这个方法中依次完成了堆类内部静态块的调用和类内部静态属性的初始化(如果存在父类,父类會优先进行初始化)不论创建多少个实例化的对象,一个类只会被初始化一次

类实例化的对象通过new操作创建,Java虚拟机保证一个类在new操莋实例化其对象之前已经完成了类的jvm加载类的顺序、链接和初始化之后Java虚拟机会调用<init>方法完成类实例化对象的初始化。这个方法会优先按照代码中顺序完成对类内部个属性的初始化之后再调用类的构造函数(如果有父类,则优先调用父类的构造函数)

  PS:需要注意嘚是上述提到的<init>和<clinit>方法都是非法的Java方法名,是由编译器命名的并不能由编码实现。

  综上所述我们大致可以得出以下结论,对于一個类在实例化一个这个类的对象时,我们可以保证以下这样的优先级进行初始化:

  类内部静态块 > 类静态属性 > 类内部属性 > 类构造函数

  最近看了几篇谈设计模型单例模式的Java实现的文章在实现一个具体的线程安全单例Java类中,一个简单且被推荐的方式使用内部静态类存儲一个静态属性这就涉及到内部静态类的初始化顺序的问题,结合想到这篇文章中也没有讨论过这个问题继而做了一些实验,代码如丅:

  实验的结果证明顺序如下:

  内部类静态属性(或静态块)会在内部类第一次被调用的时候按顺序被初始化(或执行);而类內部静态块的执行先于类内部静态属性的初始化会发生在类被第一次jvm加载类的顺序初始化的时候;类内部属性的初始化先于构造函数会發生在一个类的对象被实例化的时候。

  综合上面的结论上面这段代码的结果是什么呢?问题就留给读者们自行思考吧。

我要回帖

更多关于 jvm加载类的顺序 的文章

 

随机推荐