自己是在看视频的过程中看到一个比较有意思的面试题然后学习了一下关于jvm中类的加载连接和初始化部分的内容,感觉很有收获,所以在博客中记录一下。

  首先贴代码:

class SingleTon {public static int count1;   public static int count2 = 0;  private static SingleTon singleTon = new SingleTon(); private SingleTon() {  count1++;  count2++;  }  public static SingleTon getInstance() {  return singleTon;  }
}  public class ClassLoadTest {  public static void main(String[] args) {  SingleTon singleTon = SingleTon.getInstance();  System.out.println("count1=" + singleTon.count1);  System.out.println("count2=" + singleTon.count2);  }  }

这时的输出是:

count1=1
count2=1

当然了这个输出是我们意料之中的调用构造方法把count1和count2的值都加1所以输出是两个1,但是我们简单的改一下程序,如下:

class SingleTon {private static SingleTon singleTon = new SingleTon(); public static int count1;   public static int count2 = 0;  private SingleTon() {  count1++;  count2++;  }  public static SingleTon getInstance() {  return singleTon;  }
}  public class ClassLoadTest {  public static void main(String[] args) {  SingleTon singleTon = SingleTon.getInstance();  System.out.println("count1=" + singleTon.count1);  System.out.println("count2=" + singleTon.count2);  }  }

输出的结果是:

count1=1
count2=0

  只是把private static SingleTon singleTon = new SingleTon();  这个语句放到程序的上边怎么结果就不一样了呢?我想在实际开发中是很少这样写代码的,只有面试官才会想这么折磨人的问题。想要搞清楚为什么会发生不同的结果我们得从类的加载连接和初始化说起。

  

一、类的生命周期

  类从被加载到虚拟机内存中开始,直到卸载出内存为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和清理这7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)。我们在这里只讨论到初始化这个阶段。

  

  接下来我们依次说一说这几个阶段,之后我们再回头看那个面试题就很easy了。

二、类的加载

  类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。

  类的加载的最终产品是位于堆区内的class对象。

  Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

三、类的验证

  类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。

  类的验证的内容
    1.类文件的结构检查(确保类文件遵从java类文件的固定格式)
    2.语义检查(确保类文件符合java的语法规定,比如接口没有实现,final修饰的类没有子类等)
    3.字节码验证(确保字节码流可以被java虚拟机安全的执行)
    4.二进制兼容性的验证(确保类的相互引用是否正确,比如在Test类中speak()方法中会调用Person类中的say()方法,如果Person类中没有say()方法就会抛出NoSuchMothodError错误)

四、类的准备

  准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。注意是只有静态变量在这个时候分配内存并设置默认值。

五、类的解析

  在解析阶段,java虚拟机会把类二进制数据中的符号引用替换为直接引用。

  举一个例子:

public void go() {car.run();
}

  其中这个car.run();这段代码在这个类的二进制数据中就是符号引用。那什么是直接引用呢,其实说白了就是指针。在类的解析阶段java虚拟机会把这个符号引用替换为一个指针,该指针指向Car类的run()方法在方法区内的内存位置,而这个指针就是直接引用!

六、类的初始化

  (一)初始化这块是一块重要的内容,java尽力保证所有变量在使用之前都得到恰当的初始化,对于方法的局部变量,java以编译时错误的形式来贯彻这种保证(这是因为未初始化的局部变量更有可能是程序员的疏忽),所以局部变量并不会分配默认值。而成员变量java虚拟机会在初始化的时候给他分配空间并赋值,如果程序员写的程序没有赋值的就会给他一个默认值。

  (二)还有一点初始化说的是变量,对于方法并没有初始化这一说。。。

  (三)对于初始化顺序就是先静态后非静态,静态域按定义的先后顺序决定了初始化顺序,非静态域也是同理。

  (四)初始化的时机(主动使用):

    1.创建类的实例。
    2.访问某个类或接口的静态变量(编译期常量除外,因为java编译器在处理编译常量的时候直接就把值写到了class文件里),或者对该静态变量赋值。
    3.调用类的静态方法。
    4.反射(如Class.forName(“com.shengsiyuan.Test”))。
    5.初始化一个类的子类。
    6.Java虚拟机启动时被标明为启动类的类(Java Test)。

    除了上述六种情形,其他使用Java类的方式都被看作是被动使用,不会导致类的初始化。

  (六)当java虚拟机初始化一个类时,要求他所有的父类都已经初始化,但是这条规则并不适合接口。

  (七)调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。只是加载(把clss的数据加载到内存),并没有涉及到初始化。

  七、回头看我们遇到的问题

  第二次为什么输出是1和0呢,就直接看第二段程序了哈,第一段略。我们一条条分析,在main方法中调用了getInstance()方法,开始了对SingleTon类的加载过程。把SingleTon.class里的数据加载到内存之后,开始类的连接首先验证我们认为是正确的,然后下一步是准备,为静态变量赋默认值按定义的先后顺序依次是singleTon = null,count1 = 0,count2 = 0。然后接下来解析然后初始化,开始赋初值。这是执行singleTon = new SingleTon();调用构造方法count1 = 1,count2 = 1。然后执行count2 = 0; 因为count1没有赋值操作所以还是默认值0。所以最后的输出结果是:

count1=1
count2=0

  对于类的清理我打算另外花时间再写一篇。欢迎讨论!

  (原创,转载注明出处!)

[置顶]类的加载连接初始化相关推荐

  1. 类的加载连接和初始化

    类的加载.连接和初始化 ​ 当Java程序中需要使用到某个类时,虚拟机会保证这个类已经被加载.连接和初始化.而连接又包含验证.准备和解析这三个子过程,这个过程必须严格的按照顺序执行. 类的加载 ​ 通 ...

  2. java初始化加载类_Java 类的加载和初始化

    直接从书上抄来的,自己写一遍加深印象 类的加载 加载就是通过指定的类全限定名,获取此类的二进制字节流(可以是clazz文件或者直接内存读取或者远程网络,jar包等),然后将此二进制字节流转化为方法区的 ...

  3. java 类 加载 初始化_java类的加载与初始化

    https://blog.csdn.net/u013349237/article/details/71076617 1在命令行启动虚拟机jvm进行加载, 2用class.forname()方法进行动态 ...

  4. java虚拟机预先加载哪些类_Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  5. 类加载器 - 类的加载、连接与初始化

    类的加载.连接与初始化 系统流小说 www.kuwx.net 概述 在Java代码中,类型的加载.连接与初始化过程都是在程序运行期间完成的 类型:可以理解为一个class 加载:查找并加载类的二进制数 ...

  6. java 类的加载、连接和初始化

    JVM和类 调用Java命令运行Java程序时,该命令将会启动一条Java虚拟机进程,不管该Java程序启动了多少条线程,创建了多少个变量,它们都处于该Java虚拟机进程里,共享该JVM进程的内存区. ...

  7. Trembling ! Java类的加载过程详解(加载验证准备解析初始化使用卸载)

    [1]类的生命周期 一个类从加载进内存到卸载出内存为止,一共经历7个阶段: 加载->验证->准备->解析->初始化->使用->卸载 其中,类加载包括5个阶段: 加载 ...

  8. java jvm 加载类的顺序_java JVM-类加载静态初始化块调用顺序

    测试类加载的全过程 public class Have { static { System.out.println("加载Have");//先加载Have再调用main方法 } p ...

  9. Java必突-JVM知识专题(一): Java代码是如何跑起来的+类加载到使用的过程+类从加载到使用核心阶段(类初始化)+类加载的层级结构+什么是JVM的内存区域划分?Java虚拟机栈、Java堆内存

    前言: 该章节知识点梳理:本文主要是入门和了解jvm,不做深入 1.Java代码是如何运行起来的? 2.类加载到使用的过程? 3.验证准备和初始化的过程? 4.类从加载到使用核心阶段:初始化.类加载器 ...

最新文章

  1. centos5.5上安装oracle 11g R2[转发-参考用]
  2. 服务器怎么初始化系统,CentOS服务器怎么进行初始化
  3. 重学TCP协议(4) 三次握手
  4. 大端小端模式判断以及数据转换
  5. 求解集合A与B的差集
  6. [3]2020-IEEE Access-Batch Active Learning With Two-Stage Sampling 论文笔记
  7. 【只推荐一位】推荐一位资深Python爱好者,现任世界500强架构师
  8. java请求菜鸟快递接口返回乱码
  9. 关于计算机信息管理的照片,2021年10月青海自考计算机科学与技术(计算机信息管理方向)专业报名照片要什么格式...
  10. 如何擦除计算机连接网络的记录,如何清除上网记录 清除上网记录方法汇总
  11. mysql cpu使用率_MySQL CPU使用率高情况的原因和解决
  12. Spectral-based graph convolutional neural network
  13. 视频播放过程中做视频问答(视频弹题功能)
  14. Python学习13 ----Seaborn调色板
  15. linux生成表格的后缀,使用XSSFWork创建的xlsx后缀Excel文件无法打开
  16. 服务器上的文件夹设置ftp,设置ftp服务器上传文件夹
  17. 数据结构 笔记:图的遍历(DFS)
  18. 微信小程序引用阿里云图标的方法
  19. 学土木的考计算机二级的什么好些,学土木适合考计算机二级考什么?
  20. RT1052 ENET ENET_SendFrame调用 未发送帧

热门文章

  1. 如何学好 java?
  2. linux 默认路由 主机路由 网络路由
  3. linux查看操作系统版本信息
  4. 27岁了,目前从事软件测试,听一些说测试前途是IT里最差的,是这样吗
  5. JS双引号转义,号切割
  6. cad角度命令怎么输入_新手入门,学习CAD必须掌握,教你使用标注命令,绘图效率翻一倍...
  7. 【Linux】SSH相关命令
  8. 前端学习路线(简洁清晰,直击学习途径)
  9. 学会Zynq(2)Zynq-7000处理器的配置详解
  10. mysql按范围查找_Mysql实现按距离排序、范围查找