以前有关class文件已经写了两篇博客:html
一、【JVM虚拟机】(5)---深刻理解JVM-Class中常量池java
二、【JVM虚拟机】(6)---深刻理解Class中访问标志、类索引、父类索引、接口索引数组
那么这篇博客主要讲有关 字段表集合 相关的理解和代码示例。dom
字段表
:用于描述接口或者类中声明的变量,字段包括类级(static修饰)变量
以及实例级变量
,可是不包括局部变量(方法内部变量)。spa
##<font color=#FFD700>1、概念 </font>3d
字段表集合:包括了字段计数器
和字段数据区
如图:code
Field_info: 依次包含访问标志
(access_flags)、名称索引
(name_index)、描述符索引
(descriptor_index)、属性表集合
(attributes)几项。htm
<img src="https://img2018.cnblogs.com/blog/1090617/201904/1090617-20190411231413546-2127494420.png" style="border: 1px dashed rgb(204, 200, 200);" width="600" height="450">对象
字段修饰符
放在access_flags项目中,它与类中的access_flags项目是很是类似的,都是一个u2的数据类型.blog
<img src="https://img2018.cnblogs.com/blog/1090617/201904/1090617-20190411231458127-370081870.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="600" height="325">
跟随access_flags标志的是两项索引值:name_index
和descriptor_index
,它们都是对常量池的引用,分别表明着字段的简单名称
以及字段方法和方法的描述符
。
描述符的做用
:是用来描述字段的数据类型,方法的参数列表(包括数量,类型以及顺序)和返回值。
描述符规则
: 基本数据类型以及表明无返回值的void类型都用一个大写字符来表示,而对象类型则用字符加L加对象名的全限定名来表示:
<img src="https://img2018.cnblogs.com/blog/1090617/201904/1090617-20190411231525191-1906804832.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="600" height="400">
数组类型
:每一维度将使用一个前置的"["字符来描述.如一个定义为"java.lang.Stirng[ ]"类型的二维数组,将被记录为:"[[Ljava/lang/Stirng",一个整型数组"int[]"将被记录为"[I".
用描述符来描述方法
: 按照先参数列表,后返回值的顺序来描述,参数列表按照参数的严格顺序放在一组小括号"()"以内。
字段表集合中不会列出从父类或者父接口中继承而来的字段。 <br>
##<font color=#FFD700> 2、属性表集合-----静态field字段的初始化 </font>
在定义**属性字段
**的过程当中,咱们有时候会很天然地对 属性字段
直接赋值,以下所示:
public static final int MAX=100; public int count=0;
对于虚拟机而言,上述的两个**属性字段
**赋值的时机是不一样的:
对于非静态(即无static修饰)的属性字段的赋值将会出如今实例构造方法**<init>()**中
对于静态的属性字段,有两个选择:一、在静态构造方法**<cinit>()中进行;2 、使用ConstantValue**属性进行赋值。
Sun javac编译器对于 静态属性字段 的初始化赋值策略
1)、若是使用final和static同时修饰一个属性字段,而且这个字段是基本类型或者String类型的,那么编译器在编译这个字段的时候,会在对应的field_info结构体中 增长一个ConstantValue
类型的结构体,在赋值的时候使用这个ConstantValue
进行赋值。
2)、若是该属性字段并无被final修饰,或者不是基本类型或者String类型,那么将在类构造方法**<cinit>()**中赋值。
对于上述的public static final init MAX=100; javac编译器在编译此属性字段构建field_info结构体时,除了访问标志、名称索引、描述符索引外,会增长一个**ConstantValue
**类型的属性表。
<img src="https://img2018.cnblogs.com/blog/1090617/201904/1090617-20190411231712680-1015512537.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="580" height="450">
<br>
####一、先来个网上的例子,图片解释很好
public class Simple { private transient static final String str ="This is a test"; }
<img src="https://img2018.cnblogs.com/blog/1090617/201904/1090617-20190411231724787-940812686.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="662" height="821">
说明
一、字段计数器中的值为0x0001,表示这个类就定义了一个属性字段
二、 字段的访问标志是0x009A,这个字段的标志符有:ACC_TRANSIENT、ACC_FINAL、ACC_STATIC、ACC_PRIVATE;
三、 名称索引中的值为0x0005,指向了常量池中的第5项,为“str”,代表这个属性字段的名称是str;
四、描述索引中的值为0x0006,指向了常量池中的第6项,为"Ljava/lang/String;",代表这个field字段的数据类型是java.lang.String类型;
五、属性表计数器中的值为0x0001,代表field_info还有一个属性表;
六、属性表名称索引中的值为0x0007**,指向常量池中的第7项,为“ConstantValue”,代表这个属性表的名称是ConstantValue**,即属性表的类型是ConstantValue类型的;
七、属性长度中的值为0x0002,由于此属性表是ConstantValue类型,它的值固定为2;
八、常量值索引 中的值为0x0008,指向了常量池中的第8项,为CONSTANT_String_info类型的项,表示“This is a test” 的常量。在对此field赋值时,会使用此常量对field赋值。
####二、自测
package com.jincou.demo.domain; public class XiaoXiao { public String name = "小小"; private Integer age = 3; public static final String sex = "女"; }
接下来看16进制文件和class反编译文件
//一、这里直接截取到访问标志服后的16进制数据,从|开始表明字段集合相关16进制 00 2100 0600 0700 00|00 0300 0100 0800 0900 0000 0200 0a00 0b00 0000 1900 0c00 0900 0100 0d00 0000 0200 0e00 0100 0100 0f00 1000 0100 1100 0000 3300 0200 0100 0000 132a b700 012a 1202 b500 032a 06b8 0004 b500 05b1 0000 0001 0012 0000 000e 0003 0000 0003 0004 0004 000a 0005 0001 0013 0000 0002 0014 //二、查看 XiaoXiao.class反编译数据信息 Constant pool: #1 = Methodref #7.#21 // java/lang/Object."<init>":()V #2 = String #22 // 小小 #3 = Fieldref #6.#23 // com/jincou/demo/domain/XiaoXiao.name:Ljava/lang/String; #4 = Methodref #24.#25 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #5 = Fieldref #6.#26 // com/jincou/demo/domain/XiaoXiao.age:Ljava/lang/Integer; #6 = Class #27 // com/jincou/demo/domain/XiaoXiao #7 = Class #28 // java/lang/Object #8 = Utf8 name #9 = Utf8 Ljava/lang/String; #10 = Utf8 age #11 = Utf8 Ljava/lang/Integer; #12 = Utf8 sex #13 = Utf8 ConstantValue #14 = String #29 // 女 #15 = Utf8 <init> #16 = Utf8 ()V #17 = Utf8 Code #18 = Utf8 LineNumberTable #19 = Utf8 SourceFile #20 = Utf8 XiaoXiao.java #21 = NameAndType #15:#16 // "<init>":()V #22 = Utf8 小小 #23 = NameAndType #8:#9 // name:Ljava/lang/String; #24 = Class #30 // java/lang/Integer #25 = NameAndType #31:#32 // valueOf:(I)Ljava/lang/Integer; #26 = NameAndType #10:#11 // age:Ljava/lang/Integer; #27 = Utf8 com/jincou/demo/domain/XiaoXiao #28 = Utf8 java/lang/Object #29 = Utf8 女 #30 = Utf8 java/lang/Integer #31 = Utf8 valueOf #32 = Utf8 (I)Ljava/lang/Integer;
接下来咱们来分析从00 03开始。
//00 03 表明示成员变量的个数,此处为3个。 1)00 01 结合上表表明第一个变量的修饰符为 public 2)00 08 找常量池第8个 name 3)00 09 找常量池第9个 String 4)00 00 用来描述该变量的属性,由于这个变量没有附加属性,因此attributes_count为0,attribute_info为空。 //接下来直接分析第三个 1)00 19 结合上表 ACC_PUBLIC+ACC_STATIC+ACC_FINAL 恰好19 2)00 0c 找常量池第12个 sex 3)00 09 找常量池第9个 String 4)00 01 表明这个变量有一个附加属性 5)00 0d 找常量池第13个 ConstantValue 6)00 0000 02 属性长度 7)00 0e 找常量池第14个 女
经过这个例子咱们注意到: 1)、name = "小小"
中的小小
并无出现,这就是上一个例子所说的,由于它不是静态变量因此不属于类,而是属于对象,因此在建立对象的时候,才会出现。 2)、sex = "女" 中的女
出现了,由于它是静态属性字段,属于类级别的因此出现。
一、深刻了解java虚拟机第2版第六章
<br> <br>
只要本身变优秀了,其余的事情才会跟着好起来(少将5)
原文出处:https://www.cnblogs.com/qdhxhz/p/10693324.html