简述:

现在正在读一本《 深入理解java虚拟机——JVM高级特性与最佳实践 》的“类文件结构”一章,



阅读笔记《深入理解Java虚拟机——最高实践》

实验

实验:

写了两个简单的类,一个是普通的类A

另外一个是带有static main方法的类Test

A.java

package jvm.class_structure;

public class A {

}

在NotePad++ 其16进制代码

Java 中文word的编码问题 java文件的编码_字段



class文件各部分剖析

1.  魔数

头四个字节为Magic Number, 用来确定Class文件能够被虚拟机接受

Java 中文word的编码问题 java文件的编码_Java 中文word的编码问题_02


2. 版本号

第4个字节存储的是Class文件的版本号, 第5和第6个字节是此版本号,第7和第8个是主版本号, Java的版本是从45开始的

然而从1.0 到1.1 是45.0到45.3, 之后就是1.2 对应46, 1.3 对应47 。。。 1.6 对应50,换成16进制就是0x32

Java 中文word的编码问题 java文件的编码_字段_03


3.常量池

在之后就是常量池入口,  是占用Class文件空间最大的数据项目,是一种表类型数据项目。

常量池的索引值从1开始的,如

Java 中文word的编码问题 java文件的编码_常量池_04

表示的就是1 ~ 10号索引,0号是用来表示不引用任何一个常量池

之后便是常量池,

常量池:

       常量池主要包含字面量和符号引用,

       1)字面量比较接近于Java语言层面的常量概念,如文本字符串,被声明为final的常量值等

       2)符号引用则属于编译原理方面的概念, 包括

                  a.   类和接口的权限定名                     

                  b.   字段的名称和描述符

                  c.   方法的名称和描述符

虚拟机运行的时候,从常量池得到对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址中                                        

常量池中的每一项常量都是一个表,共有11种不相同的表结构数值

举个例子,

Java 中文word的编码问题 java文件的编码_字段_05

这是实际生成的class文件中的一部分,

第a位, 就是0x0007表示的就是第7种常量类型,为CONSTANT_Class_info,表示 类或接口的符号引用,

其后第b位和第c位0x0002,即指向了常量池中的第2项常量,它指向常量池中一个CONSTATNT_Utf8_info类型的常量,这个常量代表了这个类(或接口)的全限定名

然后查找第d位,0x01表示第1种常量,为CONSTATNT_Utf8_info,

在之后 是这个类的字符串长度是多少,第e为和第f位表示15 就是当前这种常量类型的Byte数目0x000e表示14 即长度为14

Java 中文word的编码问题 java文件的编码_字段_06


4. 访问标志

在常量池之后,有2个字节代表访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息

包括:这个Class是累还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等

当前使用了8个access_flags(总共两个byte 16位)

ACC_PUBLIC              0x0001   

ACC_FINAL                0x0010

ACC_SUPER              0x0020   是否允许使用invokespecial字节码指令,1.2之后这个位为真

ACC_INTERFACE       0x0200

ACC_ABSTRACT        0x0400    对于接口或抽象类这个标志值为真

ACC_SYNTHETIC       0x1000      标识这个类并不是有用户代码产生

ACC_ANNOTATION     0x2000

ACC_ENUM               0x4000


在测试类A.class中由于ACC_PUBLIC ,和ACC_SUPER为真

所以他的标志位应该是0x0001 | 0x0020 = 0x0021

Java 中文word的编码问题 java文件的编码_Java_07


5. 类索引和父类索引与接口索引集合

是一个u2类型的数据的集合,class中由这三项的数据7来确定这个类的继承关系。

类索引: 确定这个类的权限定吗

父类索引: 确定这个类的父类的权限定名

接口索引集合: 用来描述这个类实现了哪些接口

Java 中文word的编码问题 java文件的编码_Java_08

上图中第一项类索引为1, 父类索引为3, 由于没有实现任何接口所以第三项接口集合的大小为0


6. 字段表集合

字段表(field_info)用于描述接口或类中声明的变量

字段包括类级变量或实例级变量,但不包括在方法内部声明的变量。

诸如:字段作用域(public、private、protected)、static、final、volatile、transient

字段数据类型(基本类型、对象、数组)、字段名称。

除了各个修饰符都是布尔型的,其他诸如是什么数据类型之类,就由引用常量池中的常量来描述、

Java 中文word的编码问题 java文件的编码_字段_09

如上图,就是字段表从左至右四个部分,分别是:

    a.  容量计数器(fields_count) , 若为0x01 , 则表明这个类只有一个字段表数据

    b. access_flags ,若为0x01 , 表示为ACC_PUBLIC为真,为public

    c. 字段表名称(name_index), 这里为0x05, 为CONST_Utf8_info类型字符串, 表示变量名称

    d. 字段描述符(descriptor_index) , 这里为0x06, 指向常量池中的字符串'I' 表示一个int的变量



7. 方法表

依次包括:

访问标志、名称索引、描述符索引、属性表集合

其中方法中的代码是放在属性表中一个Code的属性中

附:

因为volatile和transient关键字不能修饰方法,所以方法表的访问标志中没有了这两项

但是增加了synchronized,native,strictfp, abstract的访问标志位ACC_*(' * ' 代指这四项)


8. 属性表

属性表集合的限制较为宽松, 不再要求各个属性表具有严格的顺序,并且只要无命名重复,可以自定义写入属性表的信息。

Java虚拟机运行时会忽略掉他不认识的属性

1) Code 属性

Java方法体里面的代码经过Javac编译之后,最终变为字节码指令存储在Code属性内,Code属性出现在方法表的属性集合中,但在接口或抽象类中就不存在Code属性

2)Exception属性

列举出方法中可能抛出的受查异常

3)LineNumberTable属性

描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。主要是如果抛出异常时,编译器会显示行号,就是这个属性的作用

4)LocalVariableTable属性

描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。用处在于当别人使用这个方法是能够显示出方法定义的参数名

5)SourseFile属性

记录生成这个Class文件的源码文件名称

抛出异常时能够显示错误代码所属的文件名

6)ConstantValue属性

通知虚拟机自动为静态变量赋值,只有被static字符修饰的变量(类变量)才可以有这项属性

7)InnerClass属性

用于记录内部类与宿主类之间的关联

8、9)Deprecated和Synthetic属性

这两个都是标志类型的布尔属性

Deprecated表示不再推荐使用,注解表示为@deprecated

Synthetic表示此字段或方法是由编译器自行添加的


这篇阅读笔记走马观花,浅尝辄止,后面还需要继续研究