foreach循环也叫增强型的for循环,他是JDK 5.0的新特性(其他特性例如泛型等)
使用方法如下:
for(type element:array){ ....//coding here }
其中关于foreach的优势与局限性在迭代(二)文章中都有指出,并且在上一篇文章提出了关于foreach如何具体实现的问题。
我们通过反编译.class文件(语法为:javap -verbose xxxx.class),得到了如下内容:
其实反编译后的信息很多,但是我只截取了最重要的两部分。至于这两部分分别代表什么,我们先来了解java的字节码怎么读取。当我讲述完成的时候,相信也是能够读懂上面信息的时候。
首先我们打开.class文件:显示如下:
很清晰的看到是一连串的十六进制组成,这就是汇编形成的字节码,接下来我们对如何解读字节码进行进一步的分析:
首先,我们来看一副图:
这幅图就是字节码的结构图,组成部分就分别是这么几个,我将分别进行阐述;
1)魔数(Magic Number)
四个字节的魔数,从我给出的.class文件看出,开头的四个字节是cafebabe,当然这是十六进制的数,但是为什么是cafebabe。看看java的图标吧:
2)版本号:
版本号四个字节,前四个为次版本号,后四个为主版本号,在此,值为0x0000 0034,解析出来就是次版本号为0,主版本号为52;那么,从反编译出来的第一幅图可以看到:
这就是反编译出来的版本号,与我们分析的一致;
至此,前八个字节很简单就被我们分析出来了。
3)常量池
接着就是常量池入口。
常量池是class文件中的资源仓库,主要包括两大类:字面量和符号引用。字面量如文本字符串,java申明中为final的值,而符号引用入类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符。 具体怎么实现在内存内存中,则是虚拟机的工作,我们这里还是不要深入比较好。
在进行常量池分析的时候,我们先了解常量池类型表:
上面描述了11中数据类型的结构,但是jdk1.7后又增加了三种,分别是(CONSTANT_MethodHandle_info,CONSTANT_MethodType_info以及CONSTANT_InvokeDynamic_info)算起来一共是14中,接下来按照demo码对他们进行解答:
0x0048:这是紧接着版本号的,因为常量池的数量不一定,所以我们在入口的时候需要定义一个u2类型的数据来记录到底有多少个常量池,该十六进制表示有71个常量池,这里要注意:该十六进制转换数值为72,但是实际只有71个常量池!! 缩影范围为1-71,第0项被设计者规定空留出来了,这是.class文件格式所规定的。
所以我们接下来要翻译71个常量池。(不可能的,我举例子出来能够引用就可以了):
#1: 0x07:tag值为7:class-info,一个U2类型缩影为:0x0002,即#2;
#2:0x01:tag值为1:utf-8info,长度:0x0019:byte长度其实就是这个描述的具体内容,是从一个字节大小扩展出去的。应该是25个字节(0x0019)。还记得我们使用16进制打开的.class文件么?旁边有一串字符,其中包含乱码,中间的某些字符串和16进制的值时一一对应的,采用的asc码。所以,这一段字符的值和16进制对应关系如截图所示:
当然长度只有25字节,所以在25字节过后,是下一个常量池信息了!也就是在07及其以后的数值已经是下一个常量池了。
上面两个例子其实就可以很好的解释了,我这个例子的常量池有点长,我这里给出另外一位博主的网站地址,他所举出的例子是很详细的。http://www.importnew.com/24088.html。
我们可以看到,我们自己分析的结果其实就是反编译出来的结果的第一部分。
4)Access_Flag访问标志:
访问标志信息包括这个.class文件是类还是接口,是不是public,是不是abstract,如果是类是否被声明成final:
下面给出这个标志表格:
而我们在给出的.class文件中会发现,此处的标志信息为0x0021。这其实是很简单,0x0020与0x0001的并集。即包含了两个标志信息,这里汇编器自动将这个类归类为超类。
5)类索引
类索引引用与确定类的全限定名
0x0001表示引用第一个索引,就是:#1.#2.iteretorTest/TestIterator.
6)超类索引
与类索引一致,确定超类的全限定名
0x0003表示引用第三个索引,就是:#3.#4. java/lang/Object;
7)接口索引
可以看到接口索引为2+n个字节,而我们的测试程序没有应用接口,所以我们的前两个字节是0x0000。那么,也不存在n的情况。但是如果有接口,这个时候后面直接跟着接口的索引。
8)字段表集合
字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。
而且,字段表也是2+n个字节,在本文的例子中,我们没有变量,所以在.class文件中,这里为0x0000;
但是,实际上,很多情况下都会涉及到包含变量。
这个时候,我们给出字段表集合,来对这段字段进行分析;
同样,用上面给出博主的文章中的例子可以在此分析一下。
下面就是方法,我们将在下一篇文章中写出,方法的解析就会相交前面的复杂很多,其中某些字符的含义需要单独解释,例如<init>,()V,我将会用一个单独的篇章进行方法的字节码解析。