Java编译之后生成的class文件有点类似C中的结构体,内部只有无符号数和表,其二进制文件是按一个个字节排列的,前八个字节为魔数;CAFEBABE,接着是版本号和次版本号,JDK只能运行低于自身版本的class文件,接着是常量池相关的数据,常量的个数是由1开始而不是0。接着是常量池中的数据,都是以_info结尾的。
常量表的内容举例
类的构造方法在Class文件中的标识
常量池之后是该类的访问标志
会将该类的所有标志值进行按位或运算得到对应的值,作为相应的标志,接下来就是该类的类信息,父类与接口信息,字段与方法信息。详细需要了解可以查书。
字节码指令
由于Java指令主要是针对操作数栈,因此通常不带参数。
ASM字节码编程
可以根据字节码文件的格式,利用ASM框架进行字节码编程从而跳过编译。
创建流程
此时没有无参构造方法需要为我们自己添加
但是这样是不符合规范的,还需要调用父类的无参构造方法。
这样才能拼凑好一个Class类文件
类加载流程
类的字节码文件并不是一下子就加载进内存的,而是在以下的情况会加载进内存
如果用到另一个类的字段,就会自动将另一个类加载进内存。
但是如果使用的是静态不可变的字段,那么该字段所属的类不会被加载,因为使用的是常量池中的内容直接替换Test.str,是编译器进行的优化,因为final字段导致其不会变化,对象数组的定义不会触发类的加载。
类的生命周期
总共有七个
加载就是要获取类的二进制比特流,可以从磁盘甚至网络中获取。、
验证就是要确认字节码文件正常不会危害虚拟机,例如检查版本或者是魔数是否为CADEBABE
准备就是为类变量分配内存,并为变量分配初始值。
解析就是将常量池中的符号引用替换为直接引用,此时所有变量指向对应内存的对象。
此时链接过程就完成,下一步就是进行真正的初始化,例如clinit就是这个时候进行的。
类加载器
Java提供了,同时也可以自己定义,对于每一个类,即使字节码文件相同,不同加载器加载结果不同,二者联合唯一确定一个类,默认都是使用JDK自带类加载器加载。