在这本书里面,在讲到类初始化的五种情况时,提及了一个比较有趣的事情。先来看看下面的代码

public class SubClass {

static{

System.err.println("I m your son");

}

public static final int name = 111;

}

这个时候如果调用SubClass.name,是根本不会触发SubClass初始化的(这里是因为name是一个常量,和下面的例子不一样,如果这里把final去掉,是会触发Subclass的初始化的,因为对于静态字段而言,如果静态字段被引用,就会调用getstatic指令和putstatic指令,那么自然就会引发类的初始化,详情看下面关于触发类初始化的五种情况)。再来看看另一种情况;

public class SuperClass {

static{

System.err.println("I am your father");

}

public static int value = 123;

}

public class SubClass extends SuperClass{

static{

System.err.println("I m your son");

}

}

这个时候如果调用SubClass.value(静态字段和静态方法是可以继承但是无法被覆盖,所以这里调用value,只会导致直接定义这个静态变量的类被初始化),同样也是不会使得SubClass这个类进行初始化。那么问题来了,到底类在什么时候会进行初始化,类的初始化顺序到底是怎样的?让我们接着往下看。

一. 类加载的过程

虚拟机加载类主要有五个过程:加载、验证、准备、解析和初始化。

加载:加载是“类加载”的一个过程,希望读者没有混淆这两个概念。

在这个过程虚拟机主要完成三件事,

 通过一个类的全限定名___[解释全限定名]___来获取此类的二进制字节流,这点上,虚拟机并没有指明要从哪里获取类的二进制字节流,因此发展出了很多不一样的加载方式。比如jar,zip等压缩包中加载,从网络获取[如Applet],或者由其他文件生成[如从JSP生成]。

 将字节流所代表的静态存储结构转化为方法区的运行时数据结构。

 在Java堆[这个没有强制规定,比如HotSpot则选择在方法区中生成这个对象]中生成一个代表这个类的java.lang.Class对象,作为程序访问方法区中的各种数据的外部入口[也就是说当常量池表中的数据被转换成运行时数据结构的时候,实际上[堆/方法区]有一个Class对象的实例可以访问到方法区的各类数据,包括常量池表,代码等]。

如果加载对象是普通的类或者接口(统称为C),则是通过类加载器(L)去加载C的二进制表示来创建。但是如果加载的是数组类,那情况就有所不同了,数组类本身不通过类加载器创建,它是由Java虚拟机直接创建的。但是数组类内部的元素类型最终还是要靠类加载器去加载。[后续可以添加类加载器的详细解释]

验证

验证是链接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机本身的安全。验证大致上有以下4个过程:

1) 文件格式验证:

a) 检查魔数,主、次版本号是否在当前虚拟机处理范围。

b) 常量池的常量是否不被支持[通过检查tag],指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量。

c) CONSTANT_Utf8_info类型的常量中是否有不符合UTF8编码的数据。

d) Class文件中各个部分及文件本身是否有被删除或者附加其他信息等等。

这个节点的主要目的是保证输入的字节流能被正确的解析并存储于方法区内,格式上符合描述一个java类型信息的要求。这个阶段是基于二进制流,只要通过了这个阶段的验证,字节流才会进入内存的方法区中存储。所以后续的三个阶段基于方法区的存储结构进行的,不会再直接操作字节流。

2) 元数据验证:这个阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。主要验证包括以下几点:

a) 这个类是否有父类(除了java.lang.Object外,所有的类都应该有父类)。

b) 这个类的父类是否继承了不允许被继承的类(被final修饰的类)。

c) 如果这个类不是抽象类,那么应该实现其父类或接口中要求实现的方法。

d) 类中的字段,方法是否与父类相矛盾(例如覆盖了父类的final字段,或者出现不符合规则的方法重载)。

这个阶段主要目的是对类的元数据信息进行语义校验,保证不存在不符合Java规范的元数据信息。

3) 字节码验证:

4) 符号引用验证:

准备

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,这些变量所使用的内存都将在方法区中分配。这里有几个值得注意的点:

1) 这里初始化的仅仅是类变量(被static修饰的变量)的初始化,并不包括实例变量。实例变量将会在对象实例化的时候随着对象一起分配在java堆中。

2) 这里所说的初始值,通常是数据类型的零值,举个例子:

public static int value = 123;

这句代码中,value在准备阶段的初始值为0,而不是123,因为这个时候还没开始执行任何的java方法。而把value的值置为123的putstatic指令是程序被编译后,存放在类构造器()方法中的。所以value置为123是在初始化[第五阶段]阶段才会执行。[还有一些其他类型的零值,可以参考虚拟机规范]

当然,上述情况也有例外的地方,如果类字段的字段属性表(参考class文件中的属性数据结构)中存在ConstatntValue[即同时被final和static修饰]属性,那么在准备阶段,变量value就会被初始化为ConstantValue属性所指定的值,例如上述变量中,编译时javac将会为value生成的ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue属性而将value赋值为123。

解析

解析阶段就是虚拟机将常量池内的符号引用[使用一组描述符来描述所引用的目标,符可以是任意形式的字面量,只要使用时能无歧义的定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中]替换为直接引用[直接引用可以是直接指向目标的指针,相对偏移量或者一个能间接定位到目标的句柄。直接引用与内存的布局有关,如果有了直接引用,则目标一定存在]的过程,符号引用在Class文件内的常量池中以CONSTANT_Fieldref_info,CONSTANT_Class_info,CONSTANT_Methodref_info等类型出现。那么,解析阶段中的直接引用于符号引用又有什么关联呢?

对同一个符号引用进行多次解析请求是很常见的,比如你在代码里面多次new同一个类。这里要分成两种情况:

1) invokeddynamic指令:这个指令的特殊之处在于,它是为了支持动态语言而存在的,也就是说,必须等到程序实际运行这条指令的时候,解析动作才能进行[目前仅使用java语言并不会生成这条指令]。相对的,其余触发的解析指定都是“静态”的,可以在刚刚完成加载阶段,还没开始执行代码时就进行解析。

2) 除了上述的指令外,虚拟机实现可以对第一次解析的结果进行缓存(在运行时常量池中记录直接引用,并把常量标识为已解析状态)。从而避免了多次解析。

解析动作主要针对“类或接口”,“字段”,“类方法”,“接口方法”,“方法类型”,“方法句柄”和“调用点限定符”7类符号引用进行[分别对应7种常量池表的CONSTATN_Class_info,CONSTATN_Fieldref_info,CONSTATN_Methodref_info,CONSTATN_InterfaceMethodref_info,CONSTATN_MethodType_info,CONSTATN_MethodHandle_info,CONSTATN_InvokeDynamic_info,后续三种和动态类型有关,目前java还是静态类型语言]。

初始化

在虚拟机中严格规定需要对类进行初始化的,有下面五种情况:

1) 遇到new,getstatic,putstatic或者invokestatic这4条字节码指令时。

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

3) 当初始化一个类,发现其父类并没有初始化时,需要先初始化父类。

4) 虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类。

5) 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有初始化,则需要先触发其初始化。

对于以上五种初始化场景,虚拟机规范中使用了“只有”,除此之外,所有的引用类的方式都不会触发初始化。

java类加载过程_java类的加载过程相关推荐

  1. 深入理解Java虚拟机二(类加载器和类的加载过程)

    类加载器子系统作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于它是否可以运行,则由Ex ...

  2. 类加载器与类的加载过程

    java虚拟机运行流程 类加载器子系统作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于 ...

  3. jvm:类加载器和类的加载过程

    public class DeadThreadTest {public static void main(String[] args) {Runnable r = () ->{System.ou ...

  4. java类加载过程_面试官:java类的加载过程

    Java 类加载机制 类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段. 其中类加载过程包括加载.验证.准备.解析和初始化五个阶段. 类的加载 ...

  5. 从JVM看类的加载过程与对象实例化过程

    一. 类的加载过程 1. 类的加载过程大致是个什么过程? 我们编写产生.java文件,这些.java文件经过Java编译器编译成拓展名为.class的文件,.class文件中保存着Java代码经转换后 ...

  6. 类的加载过程详解:加载、验证、准备、解析、初始化

    想要弄明白的知识点: 类加载的过程,加载.验证.准备.解析.初始化.每个部分详细描述. 加载阶段读入.class文件,class文件时二进制吗,为什么需要使用二进制的方式? 验证过程是防止什么问题?验 ...

  7. Java类的加载过程,类加载器,双亲委派原则

    Java一个类的加载过程: 1.加载 a.jvm会根据类名找到对应的类文件 b.进行文件内容读取 2.链接 链接主要是验证类中数据是否合法,然后把刚加载进来的类和其他类的关系建立清楚,主要有以下几个步 ...

  8. Java类的加载过程详解 面试高频!!!值得收藏!!!

    受多种情况的影响,又开始看JVM 方面的知识. 1.Java 实在过于内卷,没法不往深了学. 2.面试题问的多,被迫学习. 3.纯粹的好奇. 很喜欢一句话: 八小时内谋生活,八小时外谋发展. 望别日与 ...

  9. JVM学习笔记之-类加载子系统,类的加载与类的加载过程,双亲委派机制

    一 类加载器与类加载过程 类加载子系统作用 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识. ClassLoader只负责class文件的加载,至于 ...

最新文章

  1. 档案盒正面标签制作_2020昆明大学档案盒价格价格行情
  2. Charpter5 软件测试总结
  3. leetcode算法题--反转链表
  4. VMware发布新版vSphere6.5虚拟化平台,助力企业数字化转型
  5. Dell T620大硬盘安装Win2008R2
  6. const 在C++中的区别
  7. 旧金山参议员提议发布“封杀令”,理由是马路不为机器人所服务
  8. vue2.0 引用qrcode.js实现获取改变二维码的样式
  9. html制作花样链接卡页面_8 个「新标签页」Chrome 扩展,把 New Tab 页面玩出花样 | Matrix 精选...
  10. sklearn模型使用贝叶斯优化调参(以随机森林为例)
  11. java restrictions_Restrictions----用法
  12. postman如何改成中文版_在 Windows 上如何用 Postman 重现 Dubbo 反序列化漏洞
  13. 2017华南理工华为杯H bx值(容斥问题)
  14. 一分钟教你学会配置eslint,还在为风格单双引号,对象末位逗号要不要,引用各种报错而烦恼?(建议收藏!详细中文注释)...
  15. NYOJ241 - 字母统计
  16. 路由算法之LS算法和DV算法全面分析
  17. 一位算法工程师从30+场秋招面试中总结出的超强面经—文本检测与GAN篇(含答案)...
  18. JVM-JConsole:Java监视与管理控制台(windows)
  19. WPS Excel表格怎么启用宏功能?
  20. git错误:error: failed to push some refs to

热门文章

  1. SSM框架整合druid查看监控数据
  2. The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.
  3. 分布式应用,response导出error on submit request on future invoke、java.lang.OutOfMemoryError: Java heap space
  4. Redis数据类型--列表类型
  5. java 发送邮件_Jenkins实现自动化邮件发送踩坑记录
  6. java对象深克隆_JAVA中对象的克隆及深拷贝和浅拷贝
  7. Qt界面设计器中的界面预览与程序运行时界面不一样
  8. C语言 指针数组和数组指针区别 - C语言零基础入门教程
  9. Python 线程池 ThreadPoolExecutor(二) - Python零基础入门教程
  10. dtft变换的性质_[2018年最新整理]DTFT变换.ppt