Java class文件格式之访问标志信息_动力节点Java学院整理

class文件中的访问标志信息

位于常量池下面的2个字节是access_flags 。 access_flags 描述的是当前类(或者接口)的访问修饰符, 如public, private等, 此外, 这里面还存在一个标志位, 标志当前的额这个class描述的是类, 还是接口。access_flags 的信息比较简单, 下面列出access_flags 中的各个标志位的信息。本来写这个系列博客参考的是《深入java虚拟机》, 但是这本书比较老了, 关于java 5以后的新特性没有进行解释,这本书中指列出了5个标志值, 而最新的JVM规范是针对java 7 的, 其中加入了额外的三个标志位。 分别是ACC_SYNTHETIC, ACC_ANNOTATION 和 ACC_ENUM  。


标志名

标志值

标志含义

针对的对像

ACC_PUBLIC

0x0001

public类型

所有类型

ACC_FINAL

0x0010

final类型


ACC_SUPER

0x0020

使用新的invokespecial语义

类和接口

ACC_INTERFACE

0x0200

接口类型

接口

ACC_ABSTRACT

0x0400

抽象类型

类和接口

ACC_SYNTHETIC

0x1000

该类不由用户代码生成

所有类型

ACC_ANNOTATION 

0x2000

注解类型

注解

ACC_ENUM  

0x4000

枚举类型

枚举

其他标志就不做介绍了, 这些标志都很简单。 读者感觉比较陌生的可能是ACC_SUPER这个标志。 读者会想, 类型不能被super关键字修饰啊, 那这个ACC_SUPER是做什么的呢?表中可以看出, 它的含义是:使用新的invokespecial语义 。 invokespecial是一个字节码指令, 用于调用一个方法, 一般情况下, 调用构造方法或者使用super关键字显示调用父类的方法时, 会使用这条字节码指令。 这正是ACC_SUPER这个名字的由来。 在java 1.2之前, invokespecial对方法的调用都是静态绑定的, 而ACC_SUPER这个标志位在java 1.2的时候加入到class文件中, 它为invokespecial这条指令增加了动态绑定的功能。 这里可能有几个概念读者不是很明白, 如静态绑定, 动态绑定等, 这些概念会在以后的博客中详细介绍。

还有一点需要说明, 既然access_flags 出现在class文件中的类的层面上, 那么它只能描述类型的修饰符, 而不能描述字段或方法的修饰符, 希望读者不要将这里的access_flags 和后面要介绍的方法表和字段表中的访问修饰符相混淆。

此外, 在Java 5 的中, 引入和注解和枚举的新特性, 那么可以推测,  ACC_ANNOTATION 和 ACC_ENUM是在Java 5版本中加入的。 class文件虽然总体上保持前后一致性, 但他也不是一成不变的, 也会跟着Java版本的提升而有所改变, 但是总体来说, class文件格式还是相对稳定的, 变动的地方不是很多。

class文件中的this_class

访问标志access_flags 下面的两个字节叫做this_class, 它是对当前类的描述。 它的两个字节的数据是对常量池中的一个CONSTANT_Class_info数据项的一个索引。 CONSTANT_Class_info在上面的文章中已经介绍过了。 CONSTANT_Class_info中有一个字段叫做name_index , 指向一个CONSTANT_Utf8_info , 在这个CONSTANT_Utf8_info 中存放着当前类的全限定名。

如果当前类为Person:

package combjpowernodetest; 

public class Person { 

 int age; 

 int getAge(){
  return age;
 }
}

将Person.class反编译后, 可以在常量池中看到如下两项:

 Constant pool:
 #1 = Class    #2    // com/bjpowernode/test/Person
 #2 = Utf8    com/bjpowernode/test/Person 

.........
......... 

这两项就是当前类的信息。 其中索引为1的CONSTANT_Class_info会被class文件中的this_class所引用。 下面给出示例图(其中虚线范围内表示常量池的区域):

class文件中的super_class

super_class紧跟在this_class之后。 它和this_class一样是一个指向常量池数据项的索引。 它指向一个CONSTANT_Class_info, 这个CONSTANT_Class_info数据项描述的是当前类的超类的信息。CONSTANT_Class_info中的name_index指向常量池中的一个CONSTANT_Utf8_info ,CONSTANT_Utf8_info 中存放的是当前类的超类的全限定名。 如果没有显式的继承一个,也就是说如果当前类是直接继承Object的, 那么super_class值为0 。 我们在前面的文章中提到过, 如果一个索引值为0, 那么就说明这个索引不引用任何常量池中的数据项, 因为常量池中的数据项是从1开始的。 也就是说, 如果一个类的class文件中的super_class为0 , 那么就代表该类直接继承Object类。

下面以代码来说明:

package combjpowernodetest; 

public class Programer extends Person{ 

 Computer computer; 

 public Programer(Computer computer){
  thiscomputer = computer;
 } 

 public void doWork(){
  computercalculate();
 }
}

上面的Programer类继承自Person类。 那么反编译Programer .class , 它的常量池中会存在如下信息:

Constant pool: 

.........
......... 

 #3 = Class    #4    // com/bjpowernode/test/Person
 #4 = Utf8    com/bjpowernode/test/Person 

这两项就是当前类的父类的信息。 其中索引为3的CONSTANT_Class_info会被class文件中的super_class引用。 下面给出示例图(其中虚线范围内表示常量池的区域):

class文件中的interfaces_count和interfaces

紧接着super_class的是interfaces_count, 表示当前类所实现的接口的数量或者当前接口所继承的超接口的数量。 注意, 只有当前类直接实现的接口才会被统计, 如果当前类继承了另一个类, 而另一个类又实现了一个接口, 那么这个接口不会统计在当前类的interfaces_count中。 在interfaces_count后面是interfaces, 他可以看做是一个数组, 其中的每个数组项是一个索引, 指向常量池中的一个CONSTANT_Class_info, 这个CONSTANT_Class_info又会引用常量池中的一个CONSTANT_Utf8_info , 这个CONSTANT_Utf8_info 中存放着有当前类型直接实现或继承的接口的全限定名。 当前类型实现或继承了几个接口, 在interfaces数组中就会有几个数项与之相对应。

下面看代码示例:

package combjpowernodetest; 

public class Plane implements IFlyable, Cloneable{ 

 @Override
 public void fly() { 

 }
}

Plane类实现了一个自定义的IFlyable接口, 还实现了一个JDK中的Cloneable接口, 那么它的常量池中会有如下信息:

Constant pool: 

.........
......... 

 #5 = Class    #6    // com/bjpowernode/test/IFlyable
 #6 = Utf8    com/bjpowernode/test/IFlyable
 #7 = Class    #8    // java/lang/Cloneable
 #8 = Utf8    java/lang/Cloneable 

.........
.........

这四项数据就是当前的Plane类所实现的接口的信息。 第五项和第六项描述了Plane所实现的IFlyable接口, 第七项和第八项描述了Plane所实现的接口Cloneable接口。 下面是示意图(其中虚线范围内表示常量池的区域):

总结

主要讲解了三个部分, 分别是this_class , super_class , interfaces_count和interfaces 。 这三个数据项分别描述了当前类(就是当前class文件所在的类), 当前类所继承的超类, 和当前类所实现的接口(如果当前class文件代表的是一个接口, 那么 interfaces_count和interfaces描述的是当前接口所继承的超接口)。

这几个数据项都持有指向常量池的索引。 真实的信息都是存放在常量池中的, 只不过常量池中的这些信息会被this_class , super_class , interfaces_count和interfaces 引用。

(0)

相关推荐

  • java动态添加外部jar包到classpath的实例详解

    java动态添加外部jar包到classpath的实例详解 前言: 在项目开发过程中我们有时候需要动态的添加外部jar包,但是具体的业务需求还没有遇到过,因为如果动态添加外部jar包后,我们就需要修改业务代码,而修改代码就需要重新启动服务,那样好像就没有必要动态添加外部jar包了,怎么样才能不重新启动服务器就可以使用最新的代码我没有找到方法,如果各位知道的话给我点建议,回归主题,实现动态添加外部jar包到classpath的方法如下: String beanClassName = "com.dy

  • Java class文件格式之属性详解_动力节点java学院整理

    Code属性 code属性是方法的一个最重要的属性. 因为它里面存放的是方法的字节码指令, 除此之外还存放了和操作数栈,局部变量相关的信息. 所有不是抽象的方法, 都必须在method_info中的attributes中有一个Code属性.下面是Code属性的结构, 为了更直观的展示Code属性和method_info的包含关系, 特意画出了method_info: 下面依次介绍code属性中的各个部分. attribute_name_index指向常量池中的一个CONSTANT_Utf8_in

  • Java Class 解析器实现方法示例

    最近在写一个私人项目,名字叫做ClassAnalyzer,ClassAnalyzer的目的是能让我们对Java Class文件的设计与结构能够有一个深入的理解.主体框架与基本功能已经完成,还有一些细节功能日后再增加.实际上JDK已经提供了命令行工具javap来反编译Class文件,但本篇文章将阐明我实现解析器的思路. Class文件 作为类或者接口信息的载体,每个Class文件都完整的定义了一个类.为了使Java程序可以"编写一次,处处运行",Java虚拟机规范对Class文件进行了严

  • Java 使用getClass().getResourceAsStream()方法获取资源

    Java 使用getClass().getResourceAsStream()方法获取资源 之前想获取一个资源文件做一些处理,使用getClass().getResourceAsStream()一直拿不到文件. 具体的用法. 1 InputStream is = this.getClass().getResourceAsStream(fileName); //拿不到资源 2 InputStream is = this.getClass().getResourceAsStream("/"

  • Java中Class类的作用与深入理解

    Java中Class类的作用与深入理解 在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识.这个信息跟踪着每个对象所属的类.JVM利用运行时信息选择相应的方法执行.而保存这些信息的类称为Class.可能容易产生混淆,容易想到class.不过二者没什么关系,class不过是描述类的一个关键字.而Class却是保存着运行时信息的类. 它能做什么?Class类可以帮助我们在程序运行时分析类,说白了就是获取类中的值.可能瞬间就想到了反射,没错!Class一般就是和反射配套使

  • java.lang.NoClassDefFoundError错误解决办法

    java.lang.NoClassDefFoundError错误解决办法 前言 在日常Java开发中,我们经常碰到java.lang.NoClassDefFoundError这样的错误,需要花费很多时间去找错误的原因,具体是哪个类不见了?类明明还在,为什么找不到?而且我们很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException这两个错误搞混,事实上这两个错误是完全不同的.我们往往花费时间去不断尝试一些其他的方法去解决这

  • Java class文件格式之访问标志信息_动力节点Java学院整理

    class文件中的访问标志信息 位于常量池下面的2个字节是access_flags . access_flags 描述的是当前类(或者接口)的访问修饰符, 如public, private等, 此外, 这里面还存在一个标志位, 标志当前的额这个class描述的是类, 还是接口.access_flags 的信息比较简单, 下面列出access_flags 中的各个标志位的信息.本来写这个系列博客参考的是<深入java虚拟机>, 但是这本书比较老了, 关于java 5以后的新特性没有进行解释,这本

  • Java class文件格式之数据类型(二)_动力节点Java学院整理

    常量池中各数据项类型详解(续) (8) CONSTANT_Class_info 常量池中的一个CONSTANT_Class_info, 可以看做是CONSTANT_Class数据类型的一个实例. 他是对类或者接口的符号引用. 它描述的可以是当前类型的信息, 也可以描述对当前类的引用, 还可以描述对其他类的引用. 也就是说, 如果访问了一个类字段, 或者调用了一个类的方法, 对这些字段或方法的符号引用, 必须包含它们所在的类型的信息, CONSTANT_Class_info就是对字段或方法符号引用

  • Java中HashSet和HashMap的区别_动力节点Java学院整理

    什么是HashSet? HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象.如果我们没有重写这两个方法,将会使用这个方法的默认实现.. public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true. 什

  • Java中抽象类和接口的区别_动力节点Java学院整理

    接口 1 因为java不支持多重继承,所以有了接口,一个类只能继承一个父类,但可以实现多个接口,接口本身也可以继承多个接口. 2 接口里面的成员变量默认都是public static final类型的.必须被显示的初始化. 3 接口里面的方法默认都是public abstract类型的.隐式声明. 4 接口没有构造方法,不能被实例化. 5 接口不能实现另一个接口,但可以继承多个接口. 6 类如果实现了一个接口,那么必须实现接口里面的所有抽象方法,否则类要被定义为抽象类. 抽象类 1 如果将一个类

  • Java中HashTable和HashMap的区别_动力节点Java学院整理

    HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的区别.主要的区别有:线程安全性,同步(synchronization),以及速度. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap allows one null key and any number of null values.,而Hashtable则不行).这就是说,HashMap中如果在表中没有发现搜索键,或者如

  • 简述Java中进程与线程的关系_动力节点Java学院整理

    概述 进程与线程,本质意义上说, 是操作系统的调度单位,可以看成是一种操作系统 "资源" .Java 作为与平台无关的编程语言,必然会对底层(操作系统)提供的功能进行进一步的封装,以平台无关的编程接口供程序员使用,进程与线程作为操作系统核心概念的一部分无疑亦是如此.在 Java 语言中,对进程和线程的封装,分别提供了 Process 和 Thread 相关的一些类.本文首先简单的介绍如何使用这些类来创建进程和线程,然后着重介绍这些类是如何和操作系统本地进程线程相对应的,给出了 Java

  • Spring MVC访问静态文件_动力节点Java学院整理

    如何你的DispatcherServlet拦截"*.do"这样的有后缀的URL,就不存在访问不到静态资源的问题. 如果你的DispatcherServlet拦截"/",为了实现REST风格,拦截了所有的请求,那么同时对*.js,*.jpg等静态文件的访问也就被拦截了. 我们要解决这个问题. 目的:可以正常访问静态文件,不可以找不到静态文件报404. 方案一:激活Tomcat的defaultServlet来处理静态文件 Xml代码 <servlet-mappin

  • Java中线程的等待与唤醒_动力节点Java学院整理

    wait(), notify(), notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁.而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程:notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程. Object类中关于等待/唤醒的API详细信息如下: notify()      

  • 探索Java中的equals()和hashCode()方法_动力节点Java学院整理

    equals()和hashCode()区别?  equals():反映的是对象或变量具体的值,即两个对象里面包含的值--可能是对象的引用,也可能是值类型的值.  hashCode():计算出对象实例的哈希码,并返回哈希码,又称为散列函数.根类Object的hashCode()方法的计算依赖于对象实例的D(内存地址),故每个Object对象的hashCode都是唯一的:当然,当对象所对应的类重写了hashCode()方法时,结果就截然不同了. 之所以有hashCode方法,是因为在批量的对象比

随机推荐