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

class文件中的attributes_count和attributes

attributes_count位于class文件中methods的下面。 它占两个字节, 存储的是一个整数值, 表示class文件中属性的个数。 
attributes_count下面的是attributes, 可以把它看做一个数组, 每个数组项是一个attribute_info , 每个attribute_info 表示一个属性。attributes中有 attributes_count个attribute_info 。

需要说明的是, 属性会出现在多个地方, 不仅仅出现在顶层的ClassFile中, 也会出现在class文件中的数据项中, 如出现在field_info中, 用来描述特定字段的一些信息, 还可以出现在method_info中, 用来描述特定方法的一些信息。

属性(attribute_info)的大概格式是这样的:

其中attribute_name_index占两个字节, 它是一个指向常量池数据项的索引。 它指向一个CONSTANT_Utf8_info , 这个CONSTANT_Utf8_info 中存放的是当前属性的名字。

attribute_name_index下面的四个字节叫做attribute_length, 它表示当前属性的长度, 这个长度不包括前6个字节, 也就是说只包括属性真实信息(也就是info)的长度。

attribute_length下面的数据是info, 它的长度由上面提到的attribute_length指定, 它存放的是真实的属性数据。

下面我们会依次介绍一些重要属性, 相对不是很重要的属性会一笔带过。

ClassFile中的SourceFile属性

首先介绍一个比较简单的属性:SourceFile。 该属性出现在顶层的class文件中。 它描述了该类是从哪个源文件中编译来的, 注意, 描述的是源文件, 而不是类, 一个源文件中可以存在多个类。 它的格式如下:

前面说过, attribute_name_index指向常量池中的一个CONSTANT_Utf8_info , 这个CONSTANT_Utf8_info 中存放的是这个属性的名字字符串, 即“SourceFile” 。

attribute_length是属性信息的长度, 这里是2, 因为这个属性的info就两个字节。

sourcefile_index占两个字节, 这也是为什么attribute_length是2的原因。 sourcefile_index指向常量池中的一个CONSTANT_Utf8_info , 这个CONSTANT_Utf8_info 中存放的是生成该类的源文件的文件名, 这里的文件名不包括路径部分。

下面举例说明, 示例代码:

package combjpowernodetest; 

public class Person { 

  int age; 

  int getAge(){
    return age;
  }
}

反编译后的相关信息:

public class combjpowernodetestPerson 

 SourceFile: "Personjava" 

Constant pool: 

......... 

 #20 = Utf8        SourceFile
 #21 = Utf8        Personjava 

......... 

反编译结果中的  SourceFile: "Person.java"  一行是SourceFile属性的简单表示形式。 可以把它看做一个可读的attribute_info 。 下面常量池中的第20项的CONSTANT_Utf8_info是对这个属性的属性名(attribute_name_index)的描述 , 第21项的CONSTANT_Utf8_info是对源文件的文件名的描述。

下面是图例, 注意, 虚线范围内表示常量池区域:

ClassFile中的InnerClasses属性

InnerClasses是一个存在于顶层class文件中的属性, 它描述的是内部类和外围类的关系。  这是一个相对来说比较复杂的属性, 因为每个类可能有多个内部类, 而这些内部类中可能还有内部类, 多层嵌套。外围类中的InnerClasses属性必须描述它的所有内部类, 而内部类中的InnerClasses也必须描述它的外围类。

由于这个属性相对较为复杂, 而对于我们理解class文件又不具有很大的意义, 所以我们只是简单的介绍一下。 如果想深入理解这个属性, 请参考 《深入Java虚拟机》 第144到166页。

下面是这个属性的结构:

attribute_name_index和attribute_length就不过多介绍了, 和上面介绍的是一样的。

number_of_classes描述的是内部类的个数。

classes可以看做是一个数组, 这个数组中的每一项是一个inner_class_info, 而每个inner_class_info是对一个内部类的描述。每个 inner_class_info的结构如下:

Synthetic属性

Synthetic属性可以出现在filed_info中, method_info中和顶层的ClassFile中, 分别表示这个字段, 方法或类不是有用户代码生成的(即不存在与源文件中), 而是由编译器自动添加的。 例如, 编译器会为内部类增加一个字段, 该字段是对外部类对象的引用; 如果一个不定义构造方法, 那么编译器会自动添加一个无参数的构造方法<init>, 如果定义了静态字段或静态代码块, 还会根据具体情况, 增加静态初始化方法<clinit> 。 此外, 有些机制, 如动态代理, 会在运行时自动生成字节码文件, 由于这些类不是由源文件中编译来的, 所以这些类的class文件中会有一个Synthetic属性。

它的结构如下:

可以看到, 它没有真正的属性数据info, 它只是一个标志性的属性, 用来表示它所在的字段, 方法或类是由编译器自动添加的 。

下面以实例代码来说明, 源码如下:

package combjpowernodetest; 

public class Person { 

  static{
    Systemoutprintln("static"); 

  } 

  int age; 

  int getAge(){
    return age;
  }
}

反编译后的相关信息如下:

{
 int age;
  flags: 

 static {}; 

  ......... 

 public combjpowernodetestPerson(); 

  ......... 

 int getAge(); 

  .........
}

由反编译结果可以看出, 编译器自动生成了静态初始化方法和构造方法。 可能是因为Synthetic属性是可选的(也就是说某个版本的编译器可以选择不加入Synthetic属性) ,所以在反编译后的结果中没有发现Synthetic属性。

ConstantValue属性

ConstantValue属性出现在class文件中的field_info中, 也就是说它是一个和字段相关的属性。 每个field_info中最多只能出现一个ConstantValue属性。 此外, 要注意的是, 必须是静态字段才可以有ConstantValue属性。 这个静态字段可以是final的, 也可以不是final的。

这个属性为静态变量提供了另一种初始化的方式。 静态变量初始化的方式有两种, 一种就是现在要讲得ConstantValue属性, 另一种就是静态初始化方法<clinit> 不同的编译器和虚拟机可以有不同的实现方式。 但是如果虚拟机决定使用ConstantValue属性为静态变量赋值, 那么为这个变量的赋值动作, 必须位于执行<clinit>方法之前。

此外, 只有基本数据类型或String类型的静态变量才可以存在ConstantValue属性, 原因在下面会有说明。

下面介绍它的结构:

attribute_name_index和attribute_length就不过多介绍了, 和上面介绍的是一样的。这里的attribute_length为2 。

位于attribute_length之下的是constantvalue_index , 这是一个指向常量池中某个数据项的索引。这个常量池数据项中存放的就是当前字段的值。

这个常量池中的数据项,根据field_info描述的字段的不同, 可以是不同类型的数据项, 如果当前字段是byte, short, char, int, boolean类型, 那么这个被指向的常量池数据项就会是一个CONSTANT_Integer_info , 如果当前字段是一个long类型的字段, 那么这个被指向的常量池数据项就会是一个CONSTANT_Long_info 。 如果当前字段是是一个String类型的字段 , 那么这个被指向的常量池数据项就是一个CONSTANT_String_info 。 这里有一点需要说明, 虽然java语言支持byte, short, char, boolean类型, 但是JVM却不支持这几种类型, 表现在class文件中就是, class文件中的常量池中没有和这几个数据类型相对应的数据项, 这几中类型都被JVM在执行时当做int来对待, 表现在class文件中就是, 这几种类型都对应常量池中的CONSTANT_Integer_info 数据项。

这也说明了, 为什么只有基本数据类型和String类型的静态常量才会存在ConstantValue属性 。 因为constantvalue_index只是一个指向常量池的索引, 而其他引用类型的常量不会存在于常量池中。

下面以实例来说明, 实例代码如下:

package combjpowernodetest; 

public class Person { 

  static final int a = 1; 

  int age; 

  int getAge(){
    return age;
  }
}

反编译后的相关结果如下:

...... 

Constant pool: 

  #7 = Utf8        ConstantValue
  #8 = Integer      1 

{
 static final int a;
  flags: ACC_STATIC, ACC_FINAL
  ConstantValue: int 1 

  .........
}

可以看到, 源文件中的a字段, 是static final 的, 所以编译器为这个字段的filed_info生成了ConstantValue属性。 这个属性的示意图如下所示, 注意, 虚线范围内表示常量池区域:

Deprecated属性

Deprecated属性可以存在于filed_info中, method_info中和顶层的ClassFile中, 分别表示这个字段, 方法或类已经过时。 这个属性用来支持源文件中的@deprecated注解。 也就是说, 如果在源文件中为一个字段, 方法或类标注了@deprecated注解, 那么编译器就会在class文件中为这个字段, 方法或类生成一个Deprecated属性 。

Deprecated属性的格式如下:

和上面的属性一样, attribute_name_index属性指向一个常量池中的CONSTANT_Utf8_info 。 这个CONSTANT_Utf8_info中存放着该属性的名字 “Deprecated” 。

attribute_length永远为0 , 因为这个属性只是一个标志信息, 用来表示字段, 方法, 类已经过时, 而不具有任何实质性的属性信息。

下面以代码示例来说明, 代码如下:

package combjpowernodetest; 

public class Person { 

  int age; 

  @Deprecated
  int getAge(){
    return age;
  }
}

在getAge方法上使用了@deprecated 。 下面是反编译之后的相关信息:

...... 

nstant pool:
...... 

#18 = Utf8        Deprecated 

...... 

...... 

int getAge();
 flags:
 Deprecated: true 

 ...... 

可以看到, 在getAge方法相关的信息中, 有一行 Deprecated: true , 这说明编译器在getAge方法的method_info中加入了Deprecated属性。 常量池第18项的CONSTANT_Utf8_info中存放的是Deprecated属性的属性名“Deprecated” 。

下面是示意图, 虚线范围内表示常量池区域:

总结

本文就到此为止。 在本文中, 主要讲解了class文件中的一些属性。 这些属性可以出现在class文件中的对个地方, 用来描述一些其他信息。

(0)

相关推荐

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

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

  • 深入理解Java class文件格式_动力节点Java学院整理

    Class文件在Java体系结构中的位置和作用 对于理解JVM和深入理解Java语言, 学习并了解class文件的格式都是必须要掌握的功课. 原因很简单, JVM不会理解我们写的Java源文件, 我们必须把Java源文件编译成class文件, 才能被JVM识别, 对于JVM而言, class文件相当于一个接口, 理解了这个接口, 能帮助我们更好的理解JVM的行为:另一方面, class文件以另一种方式重新描述了我们在源文件中要表达的意思, 理解class文件如何重新描述我们编写的源文件, 对于深

  • Java class文件格式总结_动力节点Java学院整理

    我们都知道JVM能够识别的只有class格式的文件, 而源文件只是我们人能识别的, 不能被JVM识别. 那我们要在更深的层次上理解Java语言, 理解JVM, 只懂源文件是不够的, 因为虚拟机的很多的行为, 是在class文件中定义的, 而我们要理解JVM的行为, 就必须也学会JVM能理解的"语言", 那就是class文件格式 . 就像我们想要深入的了解一个外国人, 只站在自己的角度上是不可能了解他的, 只有你学会了他的语言, 才能对他更了解, 因为只有你理解了他说的话, 才能知道他做

  • Java class文件格式之常量池_动力节点Java学院整理

    常量池中各数据项类型详解 常量池中的数据项是通过索引来引用的, 常量池中的各个数据项之间也会相互引用.在这11中常量池数据项类型中, 有两种比较基础, 之所以说它们基础, 是因为这两种类型的数据项会被其他类型的数据项引用. 这两种数据类型就是CONSTANT_Utf8 和 CONSTANT_NameAndType , 其中CONSTANT_NameAndType类型的数据项(CONSTANT_NameAndType_info)也会引用CONSTANT_Utf8类型的数据项(CONSTANT_Ut

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

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

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

    CONSTANT_Integer_info 一个常量池中的CONSTANT_Integer_info数据项, 可以看做是CONSTANT_Integer类型的一个实例. 它存储的是源文件中出现的int型数据的值. 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值, 它的tag值为3, 也就是说, 当虚拟机读到一个tag值为3的数据项时, 就知道这个数据项是一个CONSTANT_Integer_info, 它存储的是int型数值的值. 紧挨着tag的下面4个字节叫做bytes,

  • Java class文件格式之特殊字符串_动力节点Java学院整理

    class文件中的特殊字符串 首先说明一下, 所谓的特殊字符串出现在class文件中的常量池中,本着循序渐进和减少跨度的原则, 首先把class文件中的特殊字符串做一个详细的介绍, 然后再回过头来继续讲解常量池. 现在我们将重点放在特殊字符串上. 特殊字符串包括三种: 类的全限定名, 字段和方法的描述符, 特殊方法的方法名. 下面我们就分别介绍这三种特殊字符串. (1) 类的全限定名 在常量池中, 一个类型的名字并不是我们在源文件中看到的那样, 也不是我们在源文件中使用的包名加类名的形式. 源文

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

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

  • Java class文件格式之方法_动力节点Java学院整理

    class文件中的fields_count和fields fields_count描述的是当前的类中定义的字段的个数, 注意, 这里包括静态字段, 但不包括从父类继承的字段. 如果当前class文件是由一个接口生成的, 那么这里的fields_count描述的是接口中定义的字段, 我们知道, 接口中定义的字段默认都是静态的.此外要说明的是, 编译器可能会自动生成字段, 也就是说, class文件中的字段的数量可能多于源文件中定义的字段的数量. 举例来说, 编译器会为内部类增加一个字段, 这个字段

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

    class文件中的attributes_count和attributes attributes_count位于class文件中methods的下面. 它占两个字节, 存储的是一个整数值, 表示class文件中属性的个数.  attributes_count下面的是attributes, 可以把它看做一个数组, 每个数组项是一个attribute_info , 每个attribute_info 表示一个属性.attributes中有 attributes_count个attribute_info

  • Java System类详解_动力节点Java学院整理

    System类是jdk提供的一个工具类,有final修饰,不可继承,由名字可以看出来,其中的操作多数和系统相关.其功能主要如下: • 标准输入输出,如out.in.err • 外部定义的属性和环境变量的访问,如getenv()/setenv()和getProperties()/setProperties() • 加载文件和类库的方法,如load()和loadLibrary(). • 一个快速拷贝数组的方法:arraycopy() • 一些jvm操作,如gc().runFinalization()

  • Java中的super关键字_动力节点Java学院整理

    一.super关键字 在JAVA类中使用super来引用父类的成分,用this来引用当前对象,如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象.怎么去引用里面的父类对象呢?使用super来引用,this指的是当前对象的引用,super是当前对象里面的父对象的引用. super关键字测试 package com.bjpowernode.test; /** * 父类 * * */ class FatherClass { public int value

  • Java异常继承结构解析_动力节点Java学院整理

    Java异常类层次结构图: 异常的英文单词是exception,字面翻译就是"意外.例外"的意思,也就是非正常情况.事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误.比如使用空的引用.数组下标越界.内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图.错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的.假若程序在运行期间出现了错误

  • Java设计模式之备忘录模式_动力节点Java学院

    定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

  • 深入理解Java中的final关键字_动力节点Java学院整理

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例.final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的. final关键字的含义? final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如

  • Java Date类常用示例_动力节点Java学院整理

    Date类 在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不便于实现国际化,所以从JDK1.1版本开始,推荐使用Calendar类进行时间和日期处理.这里简单介绍一下Date类的使用. 1.使用Date类代表当前系统时间 Date d = new Date(); System.out.println(d); 使用Date类的默认构造方法创建出的对象就代表当前时间,由于Date类覆盖了toString方法,所以可以直接输出Date类型的对象,显示的结果如下: Sun Ma

  • Java设计模式之迭代器模式_动力节点Java学院整理

    定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 类图: 如果要问Java中使用最多的一种模式,答案不是单例模式,也不是工厂模式,更不是策略模式,而是迭代器模式,先来看一段代码吧: public static void print(Collection coll){ Iterator it = coll.iterator(); while(it.hasNext()){ String str = (String)it.next(); System.out

随机推荐