目录
0.概述
在Java中,类型的加载、链接和初始化过程都是在程序运行期间完成的
这里的类型指的是我们定义的class、interfence、enum等等,这里并不涉及对象的概念,这些类型绝大多数是我们已经提前编写好的,注意是绝大部分,还有一部分是可以在运行期动态地生成出来,如动态代理,即在运行期间才创建出来,编译期间是不存在的
Java本身是一门静态类型语言,但是它的很多特性又是动态语言才能拥有的特质
类加载的5个阶段
1.类型的加载
1.1 加载阶段完成三件事
加载阶段,虚拟机需要完成三件事:
- 1.通过一个类的全限定类名来获取定义此类的二进制字节流
- 2.将这个二进制字节流读入到内存中,将其放在运行时数据区的方法区内,
- 3.在内存中创建一个java.lang.Class对象(规范并未说明Class对象位于哪里,HotSpot虚拟机将其放在了方法区中)用来封装类在方法区内的数据结构(一个类不管生成了多少实例,所有生成的这些对象它们所对应的Class对象只有这么唯一的一份,这里就是唯一的一份的Class对象),并向Java程序员提供了访问方法区内的数据结构的接口
1.2 加载.class文件的方式
加载.class文件的方式:
- 1.从本地系统中直接加载
- 2.通过网络下载.class文件,这种场景最典型的应用就是Applet
- 3.从zip,jar等归档文件中加载.class文件
- 4.从专有的数据库中提取.class文件,例如有些中间件服务器(如SAP Netweaver)可以选择把程序安装到数据库中来完成程序代码在集群间的分发
- 5.将Java源文件动态编译为.class文件(情况1:动态代理的场景中会出现。情况2:进行web开发时,jsp文件中可以放置java代码,最终jsp会被转换成serverlet,serverlet是一个java类,会被编译成.class文件)
1.3 数组类和非数组类的加载阶段的不同
非数组类加载阶段:
- 获取类的二进制字节流的动作是开发人员可控性最强的
- 可以使用系统提供的引导类加载器来完成
- 也可以使用由用户自定义的类加载器去完成
数组类加载阶段:
- 数组类本身不通过类加载器创建,它是由Java虚拟机直接创建
- 但数组类与类加载器仍然有很密切的关系,因为数组类的元素类型(指的是数组去掉所有维度的类型)最终是要靠类加载器去创建,创建过程遵循以下规则:
- 1.如果数组的组件类型(指的是数组去掉一个维度的类型)是引用类型,那就递归使用上述的加载过程去加载这个组件类型,数组将在加载该组件类型的类加载器的类名称空间上被标识(一个类必须与类加载器一起确定唯一性)
- 2.如果数组的组件类型不是引用类型(如int[]数组),Java虚拟机将会把数组标记为与引导类加载器关联
- 3.数组类的可见性与它的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为public
1.4 类的加载的最终结果
- 1.虚拟机外部的二进制字节流按照虚拟机所需的格式存储在方法区之中,方法区中的数据存储格式由虚拟机实现自行定义
- 2.位于内存中的Class对象(规范中并没有规定它是在Java堆中,HotSpot虚拟机将它放在方法区中)
2.类型的连接
- 类的加载阶段和类的连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,但这两个阶段的开始时间仍然保持着固定的先后顺序
- 类的连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。
- 将类与类之间的关系确定好,对于字节码相关的一些处理,一些验证和校验都是在连接阶段去完成的
- Java最后加载的时候一定是加载的字节码,如果字节码出现问题,虚拟机是拒