讲完了class文件中的常量池,咱们就至关于克服了class文件中最麻烦的模块了。如今,咱们来看一下class文件中紧接着常量池后面的几个东西:访问标志、类索引、父类索引、接口索引集合。html
好,让咱们来一一击破它们,看看它们究竟是什么东西。java
访问标志(access_flags)紧接着常量池后,占有两个字节,总共16位,以下图所示:jvm
当JVM在编译某个类或者接口的源代码时,JVM会解析出这个类或者接口的访问标志信息,而后,将这些标志设置到访问标志(access_flags)这16个位上。JVM会考虑以下设置以下访问表示信息:ide
a. 咱们知道,每一个定义的类或者接口都会生成class文件(这里也包括内部类,在某个类中定义的静态内部类也会单独生成一个class文件)。ui
对于定义的类,JVM在将其编译成class文件时,会将class文件的访问标志的第11位设置为1 。第11位叫作ACC_SUPER标志位;this
对于定义的接口,JVM在将其编译成class文件时,会将class文件的访问标志的第8位 设置为 1 。第8位叫作ACC_INTERFACE标志位;
luab. class文件表示的类或者接口的访问权限有public类型的和包package类型的。spa
若是类或者接口被声明为public类型的,那么,JVM将其编译成class文件时,会将class文件的访问标志的第16位设置为1 。第16位叫作ACC_PUBLIC标志符;
.netc. 类是否为抽象类型的,即咱们定义的类有没有被abstract关键字修饰,即咱们定义的类是否为抽象类。3d
若是咱们形如:
public abstract class MyClass{......}
定义某个类时,JVM将它编译成class文件的时候,会将class文件的访问标志的第7位设置为1 。第7位叫作ACC_ABSTRACT标志位。 另外值得注意的是,对于定义的接口,JVM在编译接口的时候也会对class文件的访问标志上的ACC_ABSTRACT标志位设置为 1;d. 该类是否被声明了final类型,即表示该类不能被继承。
此时JVM会在编译class文件的过程当中,会将class文件的访问标志的第12位设置为 1 。第12位叫作ACC_FINAL标志位;
e.若是咱们这个class文件不是JVM经过java源代码文件编译而成的,而是用户本身经过class文件的组织规则生成的,那么,通常会对class文件的访问标志第4位设置为 1 。经过JVM编译源代码产生的class文件此标志位为 0,第4位叫作ACC_SYNTHETIC标志位;
f. 枚举类,对于定义的枚举类如:public enum EnumTest{....},JVM也会对此枚举类编译成class文件,这时,对于这样的class文件,JVM会对访问标志第2位设置为 1 ,以表示它是枚举类。第2位叫作ACC_ENUM标志位;
g. 注解类,对于定义的注解类如:public @interface{.....},JVM会对此注解类编译成class文件,对于这样的class文件,JVM会将访问标志第3位设置为1,以表示这是个注解类,第3位叫作ACC_ANNOTATION标志位。
当JVM肯定了上述标志位的值后,就能够肯定访问标志(access_flags)的值了。实际上JVM上述标志会根据上述肯定的标志位的值,对这些标志位的值取或,便获得了访问标志(access_flags)。以下图所示:
举例:定义一个最简单的类Simple.java,使用编译器编译成class文件,而后观察class文件中的访问标志的值,以及使用javap -v Simple 查看访问标志。
使用UltraEdit查看编译成的class文件,以下图所示:
package com.louis.jvm; public class Simple { }
上述的图中黄色部分表示的是常量池部分,具体为何是常量池部分不是本文的重点,有兴趣的读者能够参考个人《Java虚拟机原理图解》系列关于常量池的博客,你就能够很轻松地识别常量它们了。
常量池后面紧跟着就是访问标志,它的十六进制值为0x0021,二进制的值为:00000000 00100001,由二进制的1的位数能够得出第十一、16位为1,分别对应ACC_SUPER标志位和ACC_PUBLIC标志位。
也能够经过一下运算:
0x0021 = 0x0001 | 0x0020, 即: 访问标志表示的标志是ACC_PUBLIC + ACC_SUPER
为了验证咱们的运算,使用javap -v Simple查看反编译信息以下:(小技巧:使用javap -v Simple指令的结果展现在命令提示符下显示不友好,通常我是使用javap -v Simple > temp.txt,将结果重定向到文件中,而后查看文件)
类索引的做用,就是为了指出class文件所描述的这个类叫什么名字。
类索引紧接着访问标志的后面,占有两个字节,在这两个字节中存储的值是一个指向常量池的一个索引,该索引指向的是CONSTANT_Class_info常量池项,
以上面定义的Simple.class 为例,以下图所示,查看他的类索引在什么位置和取什么值。
由上可知,它的类索引值为0x0001,那么,它指向了常量池中的第一个常量池项,那咱们再看一下常量池中的信息。使用javap -v Simple,常量池中有如下信息:
能够看到常量池中的第一项是CONSTANT_Class_info项,它表示一个"com/louis/jvm/Simple"的类名。即类索引是告诉咱们这个class文件所表示的是哪个类。
因为类实现的接口数目不肯定,因此接口索引集合的描述的前部分叫作接口计数器(interfaces_count),接口计数器占用两个字节,其中的值表示着这个类实现了多少个接口,紧跟着接口计数器的部分就是接口索引部分了,每个接口索引占有两个字节,接口计数器的值表明着后面跟着的接口索引的个数。接口索引和类索引和父类索引同样,其内的值存储的是指向了常量池中的常量池项的索引,表示着这个接口的彻底限定名。
举例:
定义一个Worker接口,而后类Programmer实现这个Worker接口,而后咱们观察Programmer的接口索引集合是怎样表示的。
-
/**
-
* Worker 接口类
-
* @author luan louis
-
*/
-
public
interface Worker{
-
-
public void work();
-
-
}
-
package com.louis.jvm;
-
-
public
class Programmer implements Worker {
-
-
@Override
-
public void work() {
-
System.out.println(
"I'm Programmer,Just coding....");
-
}
-
}