构造方法与静态代码块字节码指令详解
在上一次【https://www.cnblogs.com/webor2006/p/9630895.html】中已经花了很大的篇幅完整的将复杂文件中的常量相关的信息给分析完了,这次来将剩下的字节相关的信息给分析完,那接下来该分析啥了呢?先来整体回顾一下Java字节码的整体结构:
下面则一个个接着进行分析:
Access Flags:访问标记
往后数2个字节:
那它具体代表啥含义呢,此时就又得回顾一下访问标志表了,如下:
也就是类的访问标志是public和super的,对应于javap -verbose:
The Class Name:表示当前类的名字
往后数2个字节:
对应的常量池的索引为05,其内容为:
Super Class Nmae:父类名字
往后数2个字节:
对应常量池索引12,内容为:
Interfaces:接口
先往后数2个字节:
由于咱们这个类没有实现接口,所以实现的接口数量为0,所以这个忽略。
Fields:字段变量
先数2个字节来看一下该类有几个变量:
总共有3个变量,对应看一下源代码是不是这样:
那下面具体来看一下每个字段在字节码中的信息,此时又得回忆一下字段表集合了,如下:
第一个字段:
access_flags:访问标记
往后数2个字节:
上访问标志表中去找一下它代表啥含义:
呃,居然没有看到00相关的标记,咱们先看下源代码相关的字段定义就知道为啥是00了:
name_index:字段名字索引
往后数2个字节:
对应常量池索引14,内容为:
descriptor_index:描述符索引
往后数2个字节:
对应常量池索引为15,如下:
attrbutes_count:字段属性个数
往后数2个字节:
很显然该字段木有属性,所以略过,对于这个变量的分析咱们到jclasslib上对比一下是否如预期:
第二个字段:
access_flags:访问标记
往后数2个字节:
上访问标志表中去找一下它代表啥含义:
呃,貌似也木有找到02相关的修饰符,咱们看一下jclasslib所翻译的:
说明0x0002的修饰符为private,正如源码所定义的:
name_index:字段名字索引
往后数两个字节:
对应常量池索引为16,如下:
如源文件:
descriptor_index:描述符索引
往后数2个字节:
对应常量池过些17:
attrbutes_count:字段属性个数
往后数2个字节:
说明该字段木有属性。
第三个字段:
access_flags:访问标记
往后数2个字节:
上访问标志表中去找一下它代表啥含义:
呃,貌似也木有找到02相关的修饰符,咱们看一下jclasslib所翻译的:
对应一下源码:
name_index:字段名字索引
往后数2个字节:
对应于常量池18:
descriptor_index:描述符索引
往后数2个字节:
对应于常量池19:
attrbutes_count:字段属性个数
往后数2个字节:
也没有属性。
至此为止,三个字段就分析完了~~
Methods:方法
接下来则到方法相关的信息了,先数2个字节来查看一下该类总共有几个方法:
总共有6个方法,咱们看一下jclasslib翻译的是否也是6个:
其中最后一个"<clinit>"是对静态变量进行初始化的,只要类中定义的静态变量系统就为自动为其生成一个该方法,下面一个个方法来分析:
此时又得回忆一下方法表结构了,如下:
第一个方法:
access_flags:访问标志
往后数2个字节:
上访问标志表中去找一下它代表啥含义:
说明方法的访问标志是public的。
name_index:方法名索引:
往后数2个字节:
对应常量池索引为20:
刚好跟jclasslib是对应的:
descriptor_index:方法描述符索引:
往后数2个字节:
对应于常量池索引21:
attribute_cout:方法属性个数:
往后数2个字节:
说明这次方法有一个属性,查看一下jclasslib:
有一个code属性,所以此时就得回忆一下code属性相关的结构了,如下:
attribute_name_index:
往后往两个字节:
对应常量池22,如下:
attribute_length:
往后数四个字节:
也就是属性的长度为66,也就是往后数66个字节, 这里就不一个个字节进行分析了,由于之前已经详细分析过了,这里主要是研读一下助记符相关的东东,如下:
先查看一下官方对它的解释:
其实这里也就是推送“Welcome”,继续下:
通过这个分析,也说明对于成员变量的赋值并非是按源代码的先后顺序执行的,而是会在构造方法中进行赋值。然而发现在这个构造方法中并没对静态成员变量进行赋值,如下:
因为它是在之后的<clinit>当中进行赋值的,这个需要特别注意!
这里思考一个问题:既然我们如果没有显示提供构造方法,编译会自动生成一个默认的构造方法,那如果我们显示的声明了无参构造方法在字节码上又会有区别呢?
编译之后重新用jclasslib打开,发现跟不显示指定生成的字节码信息<init>是一模一样的,没任何区别,说明其类的成员变量依然是在显示指它的默认构造方法。
那如果咱们显示指定多个构造方法那字节码又会如何呢,试试:
编译之后用jclasslib重新打开,如下:
其观察一下它们俩的code信息有何变化:
发现除了标红处带参数多余了一个打印语句之后,其它居然木有任何变化,那说明是将成员变量的赋值操作放到了每一个构造方法当中了。
第二个方法:
这里也直接看一下jclasslib直观的描述,看一下它的code信息:
对应于源代码:
如源代码:
第三个方法:
源代码为:
看一下它的code属性:
第四个方法:
源代码为:
看一下它的code属性:
其中这个字节在之前也已经看过,这里重点观注monitorenter和monitorexit,先看一下官方对它的解释:
接着来看一下monitorexit:
所以通过了解monitorenter和monitorexit的官方助字符的说明,可以更好的理解线程同步相关的概念。
第五个方法:
源代码:
对应的code属性:
由于该方法是空代码,不多说了。
第六个方法:
可见确实是静态变量的赋值操作是在<clinit>当中,而且该方法是编译器生成的,那如果对于静态代码块,那字节码的表现又如何呢?
编译之后重新用jclasslib刷新一下:
Attributes:类的属性
这个就直接看一下jclasslib就成了,因为之前也详细分析过,比较简单:
至此!对于这个稍复杂一点的字节码文件完整的又分析了一遍,加上上一次的完整字节码分析,对于字节码这块貌似觉得非常之亲切啦~~