转载

Java类文件结构详解

链接:

  • 官方文档

1. ClassFile结构

类文件由单个ClassFile结构组成

全局规范:

  1. 任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,类或接口并不一定都得定义在文件里(譬如类或接口也可以通过类加载器直接生成)。本章中,只是通俗地将任意一个有效的类或接口所应当满足的格式称为“Class文件格式”,实际上它并不一定以磁盘文件的形式存在。“Class文件”应当是一串二进制的字节流,无论以何种形式存在。
  2. Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,当遇到需要占用8位字节以上空间的数据项时,则会按照 高位在前(Big-Endian) 的方式分割成若干个8位字节进行存储。无符号数据类型最大占8个字节。
  3. Class文件中存储数据的类型:无符号数和复合数据类型。
  4. 无符号数(基本数据类型) :以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
  5. 复合数据类型 :如下图中constant pool,可以看成是多个cp info项(常量项)组成的表。

文件中的数据项,无论是顺序还是数量,甚至于数据存储的字节序(Byte Ordering,Class文件中字节序为Big-Endian)这样的细节,都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何,都不允许改变。

ClassFile {  
    u4             magic; //Class文件的标志
    u2             minor_version; //Class的小版本号
    u2             major_version; //Class的大版本号
    u2             constant_pool_count; //常量池的数量
    cp_info        constant_pool[constant_pool_count-1]; //常量池
    u2             access_flags; //Class的访问标记
    u2             this_class; //当前类
    u2             super_class; //父类
    u2             interfaces_count; //接口
    u2             interfaces[interfaces_count]; //一个类可以实现多个接口
    u2             fields_count; //Class文件的字段属性
    field_info     fields[fields_count]; //一个类会可以有个字段
    u2             methods_count; //Class文件的方法数量
    method_info    methods[methods_count]; //一个类可以有个多个方法
    u2             attributes_count; //此类的属性表中的属性数
    attribute_info attributes[attributes_count]; //属性表集合
}

1.1 魔数

u4             magic; //Class文件的标志

The magic item supplies the magic number identifying the  class file format; it has the value  0xCAFEBABE .

每个 Class 文件的头四个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接收的 Class 文件。

程序设计者很多时候都喜欢用一些特殊的数字表示固定的文件类型或者其它特殊的含义。

1.2 Class文件版本

    u2             minor_version; //Class的小版本号
    u2             major_version; //Class的大版本号

紧接着魔数的四个字节存储的是 Class 文件的版本号:第五和第六是次版本号,第七和第八是主版本号。

高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。

1.3 类文件常量池

    u2             constant_pool_count; //常量池的数量
    cp_info        constant_pool[constant_pool_count-1]; //常量池

紧接着主次版本号之后的是常量池,常量池的数量是 constant pool count-1(常量池计数器是从1开始计数的,将第0项常量空出来是有特殊考虑的,索引值为0代表“不引用任何一个常量池项”)。

常量池主要存放两大常量:

字面量和符号引用。字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念。包括下面三类常量:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

哪些字面量会进入常量池中?

  1. 字符串
  2. 常量(final修饰的成员变量  局部变量不行 )
  3. 非final修饰的float、double、long基本类型
public class Test {

    private int int_num = 12;
    private byte byte_num = 121;
    private short short_num = 30;
    private char char_num = 'a';
    private float float_num = 45.3f; //字面量
    private double double_num = 39.8; //字面量
    private long long_num = 2323L; //字面量
    private boolean boolean_flag = true;
    private long long_delay_num;

    public void test() {
        final int a_test = 123;
        String b_test = "b_test_part"; //字面量
        this.long_delay_num = 5555L; //字面量
    }
}

所有的常量项在常量池表都有以下结构:

cp_info {  
    u1 tag;
    u1 info[];
}

以一个字节的tag开始,代表当前常量属于哪种常量类型,共有14种类型(见 Table. Constant pool tags )。

info数组的内容格式随tag不同而不同,每个tag字节后必须跟两个或多个字节,以提供有关特定常数的信息。

Table. Constant pool tags

| Constant Type | Value | 描述 | | --- | --- | --- | | CONSTANT_Class | 7 | 类或接口的符号引用 | | CONSTANT_Fieldref | 9 | 字段的符号引用 | | CONSTANT_Methodref | 10 | 类中方法的符号引用 | | CONSTANT_InterfaceMethodref | 11 | 接口中方法的符号引用 | | CONSTANT_String | 8 | 字符串类型字面量 | | CONSTANT_Integer | 3 | 整型字面量 | | CONSTANT_Float | 4 | 浮点型字面量 | | CONSTANT_Long | 5 | 长整型字面量 | | CONSTANT_Double | 6 | 双精度浮点型字面量 | | CONSTANT_NameAndType | 12 | 字段或方法的符号引用 | | CONSTANT_Utf8 | 1 | UTF-8编码的字符串 | | CONSTANT_MethodHandle | 15 | 方法句柄 | | CONSTANT_MethodType | 16 | 方法类型 | | CONSTANT_InvokeDynamic | 18 | 动态方法调用点 |

1.3.1 The CONSTANT Class info Structure

CONSTANT_Class_info {  
    u1 tag;
    u2 name_index;
}

name index: 指向CONSTANT Utf8_info常量项的合法索引值,表示一个二进制类或接口名( Binary Class and Interface Names )。

For example, the normal binary name of class _ Thread is  java.lang.Thread . In the internal form used in descriptors in the  class file format, a reference to the name of class  Thread is implemented using a  CONSTANT_Utf8_info structure representing the string  java/lang/Thread ._

For example, the class name representing the two-dimensional array type _ int[][] is  [[I , while the class name representing the type  Thread[] is  [Ljava/lang/Thread; ._ _An array type descriptor is valid only if it represents 255 or fewer dimensions.

1.3.2 The CONSTANT Fieldref info, CONSTANT Methodref info, and CONSTANT InterfaceMethodref info Structures

CONSTANT_Fieldref_info {  
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

CONSTANT_Methodref_info {  
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

CONSTANT_InterfaceMethodref_info {  
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
  • class_index

The value of the class_index item must be a valid index into the  constant_pool table. The  constant_pool entry at that index must be a  CONSTANT_Class_info structure ( §4.4.1 ) representing a class or interface type that has the field or method as a member.

  1. The  class_index  item of a  CONSTANT_Methodref_info  structure must be a class type, not an interface type.
  2. The  class_index  item of a  CONSTANT_InterfaceMethodref_info  structure must be an interface type.
  3. The  class_index  item of a  CONSTANT_Fieldref_info  structure may be either a class type or an interface type.
  4. name and type_index

The value of the name_and_type_index item must be a valid index into the  constant_pool table. The  constant_pool entry at that index must be a  CONSTANT_NameAndType_info structure ( §4.4.6 ). This  constant_pool entry indicates the name and descriptor of the field or method.

In a CONSTANT_Fieldref_info , the indicated descriptor must be a field descriptor ( §4.3.2 ). Otherwise, the indicated descriptor must be a method descriptor ( §4.3.3 ).

If the name of the method of a CONSTANT_Methodref_info structure begins with a ' < ' (' /u003c '), then the name must be the special name  <init> , representing an instance initialization method ( §2.9 ). The return type of such a method must be  void .

1.3.3 The CONSTANT String info Structure

CONSTANT_String_info {  
    u1 tag;
    u2 string_index;
}

string index: 指向CONSTANT Utf8_info常量项的合法索引值,表示一个字符串字面量。

The value of the string_index item must be a valid index into the  constant_pool table. The  constant_pool entry at that index must be a  CONSTANT_Utf8_info structure ( §4.4.7 ) representing the sequence of Unicode code points to which the  String object is to be initialized.

1.3.4 The CONSTANT Integer info and CONSTANT Float info Structures

CONSTANT_Integer_info {  
    u1 tag;
    u4 bytes;
}

CONSTANT_Float_info {  
    u1 tag;
    u4 bytes;
}

bytes: 按照高位在前存储的int值/float值

1.3.5 The CONSTANT Long info and CONSTANT Double info Structures

CONSTANT_Long_info {  
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}

CONSTANT_Double_info {  
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}

high bytes low bytes: 按照高位在前存储的long值/double值

1.3.6 The CONSTANT NameAndType info Structure

CONSTANT_NameAndType_info {  
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}
name index: 指向CONSTANT Utf8

info常量项的合法索引值,表示该字段或方法名称。

descriptor

index: 指向CONSTANT Utf8 info常量项的合法索引值,表示该字段或方法描述符。

1.3.7 The CONSTANT Utf8 info Structure

CONSTANT_Utf8_info {  
    u1 tag;
    u2 length;
    u1 bytes[length];
}

UTF-8编码的字符串长度和字节数组

1.3.8 The CONSTANT MethodHandle info Structure

CONSTANT_MethodHandle_info {  
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}
  • reference_kind

The value of the reference_kind item must be in the range 1 to 9. The value denotes the  kind of this method handle, which characterizes its bytecode behavior ( §5.4.3.5 ).

  • reference_index

The value of the reference_index item must be a valid index into the  constant_pool table. The  constant_pool entry at that index must be as follows:

  • If the value of the  reference_kind  item is 1 ( REF_getField ), 2 ( REF_getStatic ), 3 ( REF_putField ), or 4 ( REF_putStatic ), then the  constant_pool  entry at that index must be a  CONSTANT_Fieldref_info  ( §4.4.2 ) structure representing a field for which a method handle is to be created.
  • If the value of the  reference_kind  item is 5 ( REF_invokeVirtual ) or 8 ( REF_newInvokeSpecial ), then the  constant_pool  entry at that index must be a  CONSTANT_Methodref_info  structure ( §4.4.2 ) representing a class's method or constructor ( §2.9 ) for which a method handle is to be created.
  • If the value of the  reference_kind  item is 6 ( REF_invokeStatic ) or 7 ( REF_invokeSpecial ), then if the  class  file version number is less than 52.0, the  constant_pool  entry at that index must be a  CONSTANT_Methodref_info  structure representing a class's method for which a method handle is to be created; if the  class  file version number is 52.0 or above, the  constant_pool  entry at that index must be either a  CONSTANT_Methodref_info  structure or a  CONSTANT_InterfaceMethodref_info  structure ( §4.4.2 ) representing a class's or interface's method for which a method handle is to be created.
  • If the value of the  reference_kind  item is 9 ( REF_invokeInterface ), then the  constant_pool  entry at that index must be a  CONSTANT_InterfaceMethodref_info  structure representing an interface's method for which a method handle is to be created.

If the value of the reference_kind item is 5 ( REF_invokeVirtual ), 6 ( REF_invokeStatic ), 7 ( REF_invokeSpecial ), or 9 ( REF_invokeInterface ), the name of the method represented by a  CONSTANT_Methodref_info structure or a  CONSTANT_InterfaceMethodref_info structure must not be  <init> or  <clinit> .

If the value is 8 ( REF_newInvokeSpecial ), the name of the method represented by a  CONSTANT_Methodref_info structure must be  <init> .

1.3.9 The CONSTANT MethodType info Structure

CONSTANT_MethodType_info {  
    u1 tag;
    u2 descriptor_index;
}

descriptor index: 指向CONSTANT Utf8_info常量项的合法索引值,表示方法的描述符

1.3.10 The CONSTANT InvokeDynamic info Structure

CONSTANT_InvokeDynamic_info {  
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}

*bootstrap_method_attr_index: *

The value of the  bootstrap_method_attr_index item must be a valid index into the  bootstrap_methods array of the bootstrap method table ( §4.7.23 ) of this  class file.

name and type index:

指向CONSTANTNameAndType_info常量项的有效索引,表示方法名和方法描述符。

1.4 访问标志 access_flags

在常量池结束之后,紧接着的2个字节代表访问标志(access_flag),这个标志用于识别一些类或接口层次的访问信息,包括:这个Class是类还是接口,是否定义为public类型,abstract类型,如果是类的话,是否声明为final,等等。每种访问信息都由一个十六进制的标志值表示,如果同时具有多种访问信息,则得到的标志值为这几种访问信息的标志值的逻辑或。

类访问和属性修饰符:

| Flag Name | Value | Interpretation | | --- | --- | --- | | ACC_PUBLIC | 0x0001 | Declared  public ; may be accessed from outside its package. | | ACC_FINAL | 0x0010 | Declared  final ; no subclasses allowed. | | ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the  invokespecial instruction. | | ACC_INTERFACE | 0x0200 | Is an interface, not a class. | | ACC_ABSTRACT | 0x0400 | Declared  abstract ; must not be instantiated. | | ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. | | ACC_ANNOTATION | 0x2000 | Declared as an annotation type. | | ACC_ENUM | 0x4000 | Declared as an  enum type. |

1.5 当前类索引,父类索引与接口索引集合

    u2             this_class;//当前类
    u2             super_class;//父类
    u2             interfaces_count;//接口
    u2             interfaces[interfaces_count];//一个雷可以实现多个接口

类索引(this class)和父类索引(super class)都是一个u2类型的数据,而接口索引集合(interfaces)则是一组u2类型的数据集合,Class文件中由这三项数据来确定这个类的继承关系。

类索引、父类索引和接口索引集合都按照顺序排列在访问标志之后,类索引和父类索引两个u2类型的索引值表示,它们各自指向一个类型为COMNSTANT Class info的类描述符常量,通过该常量中的索引值找到定义在COMNSTANT Utf8 info类型的常量中的全限定名字符串。

而接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句(如果这个类本身是个接口,则应当是extend语句)后的接口顺序从左到右排列在接口的索引集合中。

1.6 字段集合 fields

    u2             fields_count;//Class 文件的字段的个数
    field_info     fields[fields_count];//一个类会可以有个字段

字段结构(field_info)用于描述接口或类中声明的变量。字段包括了类级变量或实例级变量,但不包括在方法内声明的变量。字段的名字、数据类型、修饰符等都是无法固定的,只能引用常量池中的常量来描述。下面是字段结构的格式

field_info {  
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
  • access_flags

字段的作用域( public , private , protected 修饰符),是实例变量还是类变量( static 修饰符),可否被序列化(transient 修饰符),可变性(final),可见性(volatile 修饰符,是否强制从主内存读写)。

| Flag Name | Value | Interpretation | | --- | --- | --- | | ACC_PUBLIC | 0x0001 | Declared  public ; may be accessed from outside its package. | | ACC_PRIVATE | 0x0002 | Declared  private ; usable only within the defining class. | | ACC_PROTECTED | 0x0004 | Declared  protected ; may be accessed within subclasses. | | ACC_STATIC | 0x0008 | Declared  static . | | ACC_FINAL | 0x0010 | Declared  final ; never directly assigned to after object construction (JLS §17.5). | | ACC_VOLATILE | 0x0040 | Declared  volatile ; cannot be cached. | | ACC_TRANSIENT | 0x0080 | Declared  transient ; not written or read by a persistent object manager. | | ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. | | ACC_ENUM | 0x4000 | Declared as an element of an  enum . |

  • name_index

对CONSTANT Utf8 info常量项的引用,表示字段的名称;

这里对应的是简单名称:

简单名称是指没有类型或参数修饰的方法或字段名称,如果一个类中有这样一个方法boolean  get(int name)和一个变量private final static int m,则他们的简单名称则分别为get() 和 m。

  • descriptor_index

对CONSTANT Utf8 info常量项的引用,表示字段描述符;

而描述符的作用则是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序等)和返回值的。根据描述符规则,详细的描述符标示字的含义如下表所示

| FieldType term | Type | Interpretation | | --- | --- | --- | | B | byte | signed byte | | C | char | Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16 | | D | double | double-precision floating-point value | | F | float | single-precision floating-point value | | I | int | integer | | J | long | long integer | | L ClassName ; | reference | an instance of class  ClassName | | S | short | signed short | | Z | boolean | true or  false | | [ | reference | one array dimension |

The field descriptor of an instance variable of type int is simply  I . The field descriptor of an instance variable of type  Object is  Ljava/lang/Object; . Note that the internal form of the binary name for class  Object is used. The field descriptor of an instance variable of the multidimensional array type  double[][][] is  [[[D .

对于方法描述符:

用方法描述符描述方法时,按照先参数后返回值的顺序描述,参数要按照严格的顺序放在一组小括号内,如方法Object m(int i, double d, Thread t) {...}的描述符为(IDLjava/lang/Thread;)Ljava/lang/Object。

  • attributes_count

一个字段还会拥有一些额外的属性,attributes_count 存放属性的个数;

  • attributes[attributes_count]

存放具体属性具体内容。

比如,如果在类中有如下字段的声明: final int i = 123;那就会存在一项名为ConstantValue的属性,它指向常量123 。关于attribute_info的详细内容,在后面关于属性集合中会有详细介绍。

最后需要注意一点:字段集合中不会列出从父类或接口中继承而来的字段,但有可能列出原本Java代码中不存在的字段。比如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。

1.7 方法集合 methods

    u2             methods_count;//Class 文件的方法的数量
    method_info    methods[methods_count];//一个类可以有个多个方法

method_info结构:

method_info {  
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

table. ** access flags**_

| Flag Name | Value | Interpretation | | --- | --- | --- | | ACC_PUBLIC | 0x0001 | Declared  public ; may be accessed from outside its package. | | ACC_PRIVATE | 0x0002 | Declared  private ; accessible only within the defining class. | | ACC_PROTECTED | 0x0004 | Declared  protected ; may be accessed within subclasses. | | ACC_STATIC | 0x0008 | Declared  static . | | ACC_FINAL | 0x0010 | Declared  final ; must not be overridden ( §5.4.5 ). | | ACC_SYNCHRONIZED | 0x0020 | Declared  synchronized ; invocation is wrapped by a monitor use. | | ACC_BRIDGE | 0x0040 | A bridge method, generated by the compiler. | | ACC_VARARGS | 0x0080 | Declared with variable number of arguments. | | ACC_NATIVE | 0x0100 | Declared  native ; implemented in a language other than Java. | | ACC_ABSTRACT | 0x0400 | Declared  abstract ; no implementation is provided. | | ACC_STRICT | 0x0800 | Declared  strictfp ; floating-point mode is FP-strict. | | ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |

注意:因为 volatile 修饰符和 transient 修饰符不可以修饰方法,所以方法表的访问标志中没有这两个对应的标志,但是增加了 synchronizednativeabstract 等关键字修饰方法,所以也就多了这些关键字对应的标志。

方法里的Java代码,经过编译器编译成字节码指令后,存放在方法属性集合中一个名为Code的属性里。

与字段集合对应,如果父类方法在子类中没有被覆写,方法集合中就不会出现来自父类的方法信息。但同样,有可能会出现由编译器自动添加的方法,最典型的便是类构造器“ ”方法和实例构造器“ ”方法。

在Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称外,还要求必须拥有一个与原方法不同的特征签名,特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里无法仅仅依靠返回值的不同来对一个已有方法进行重载。

1.8 属性集合 attributes

   u2             attributes_count;//此类的属性表中的属性数
   attribute_info attributes[attributes_count];//属性表集合

attribute_info结构:

attribute_info {  
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

attribute name index: 指向CONSTANT Utf8 info的索引值,表示属性名称。

attribute_length: 每个属性值的结构是完全可以自定义的,只需说明属性值所占用的位数长度即可。

在 Class 文件,字段集合,方法集合中都可以携带自己的属性集合,以用于描述某些场景专有的信息。与 Class 文件中其它的数据项目要求的顺序、长度和内容不同,属性集合的限制稍微宽松一些,不再要求各个属性具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。

JVM规范中预定义了多项JVM应能识别的属性( 官方说明 )。

  1. Code属性 *_   (使用位置:method_info)*_

Java程序方法体中的代码讲过Javac编译后,生成的字节码指令便会存储在Code属性中,但并非所有的方法表都必须存在这个属性,比如接口或抽象类中的方法就不存在Code属性。

结构如下:

Code_attribute {  
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

attribute name index是一项指向CONSTANT Utf8 info型常量的索引,常量值固定为“Code”,它代表了该属性的名称。attribute_length指示了属性值的长度,由于属性名称索引与属性长度一共是6个字节,所以属性值的长度固定为整个属性表的长度减去6个字节。

max stack代表了操作数栈深度的最大值,max locals代表了局部变量表所需的存储空间,它的单位是Slot,并不是在方法中用到了多少个局部变量,就把这些局部变量所占Slot之和作为max_locals的值,原因是局部变量表中的Slot可以重用。

code length和code用来存储Java源程序编译后生成的字节码指令。code用于存储字节码指令的一系列字节流,它是u1类型的单字节,因此取值范围为0x00到0xFF,那么一共可以表达256条指令,目前,Java虚拟机规范已经定义了其中200条编码值对应的指令含义。code length虽然是一个u4类型的长度值,理论上可以达到2^32-1,但是虚拟机规范中限制了一个方法不允许超过 65535条字节码指令 ,如果超过了这个限制,Javac编译器将会拒绝编译。

字节码指令之后是这个方法的显式异常处理表集合(exception

table),它对于Code属性来说并不是必须存在的。

它包含四个字段,这些字段的含义为:如果字节码从第start

pc行到第end pc行之间(不含end pc行)出现了类型为catch type或其子类的异常(catch type为指向一个CONSTANT Class info型常量的索引),则转到第handler pc行继续处理,当catch type的值为0时,代表人和的异常情况都要转到handler_pc处进行处理。异常表实际上是Java代码的一部分,编译器使用异常表而不是简单的跳转命令来实现Java异常即finally处理机制,也因此,finally中的内容会在try或catch中的return语句之前执行,并且在try或catch跳转到finally之前,会将其内部需要返回的变量的值复制一份副本到最后一个本地变量表(LocalVariableTable)的Slot中,也因此便有了如下的情况

try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况:    情况一:如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。    情况二:如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。    情况三:如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况:        1)如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。        2)如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。

Code属性是Class文件中最重要的一个属性,如果把一个Java程序中的信息分为代码和元数据两部分,那么在整个Class文件里,Code属性用于描述代码,所有的其他数据项目都用于描述元数据。

  1. Exception属性 *_   (使用位置:method_info)*_
Exceptions_attribute {  
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_exceptions;
    u2 exception_index_table[number_of_exceptions];
}

这里的Exception属性的作用是列举出方法中可能抛出的受查异常,也就是方法描述时在throws关键字后面列举的异常。它的结构很简单,只有attribute name index、attribute length、number of exceptions、exception index_table四项,从字面上便很容易理解。

  1. LineNumberTable属性 *_ (使用位置:Code)*_

    它用于描述Java源码行号与字节码行号之间的对应关系。

  2. LocalVariableTable属性 *_ (使用位置:Code)*_

它用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的对应关系。

  1. SourceFile属性 *_   (使用位置:ClassFile)*_

它用于记录生成这个Class文件的源码文件名称。

  1. ConstantValue属性 *_   (使用位置:filed_info)*_

只有被final修饰的成员变量才有ConstantValue属性

Java类文件结构详解
  1. InnerClasses属性 *_   (使用位置:filed_info)*_

该属性用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类,那么编译器将会为它及它所包含的内部类生成InnerClasses属性。

  1. Deprecated属性 *_   (使用位置:ClassFile filed_info method_info)*_

该属性用于表示某个类、字段和方法,已经被程序作者定为不再推荐使用,它可以通过在代码中使用@Deprecated注释进行设置。

  1. Synthetic属性 *_   (使用位置:filed_info method_info)*_

该属性代表此字段或方法并不是Java源代码直接生成的,而是由编译器自行添加的,如this字段和实例构造器、类构造器等。

2. 查看类文件结构的工具

2.1 jclasslib

在Idea中安装jclasslib Bytecode viewer插件

在上文中介绍字面量时使用了Test类 可以看到这样的结构

Java类文件结构详解

2.2 javap

javap分析class文件用法:javap -v class文件名

在上文中介绍字面量时使用了Test类 执行:

javap -v Test.class

可以具体的看到常量池的信息

Classfile /Users/yulian/soho/projects/road-java/road-java-pure/target/classes/com/pioneerzcy/pure/literal/Test.class  
  Last modified 2019-10-25; size 877 bytes
  MD5 checksum b221862f6c7fe428efef23582a5da234
  Compiled from "Test.java"
public class com.pioneerzcy.pure.literal.Test  
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:  
   #1 = Methodref          #20.#51        // java/lang/Object."<init>":()V
   #2 = Fieldref           #19.#52        // com/pioneerzcy/pure/literal/Test.int_num:I
   #3 = Fieldref           #19.#53        // com/pioneerzcy/pure/literal/Test.byte_num:B
   #4 = Fieldref           #19.#54        // com/pioneerzcy/pure/literal/Test.short_num:S
   #5 = Fieldref           #19.#55        // com/pioneerzcy/pure/literal/Test.char_num:C
   #6 = Float              45.3f
   #7 = Fieldref           #19.#56        // com/pioneerzcy/pure/literal/Test.float_num:F
   #8 = Double             39.8d
  #10 = Fieldref           #19.#57        // com/pioneerzcy/pure/literal/Test.double_num:D
  #11 = Long               2323l
  #13 = Fieldref           #19.#58        // com/pioneerzcy/pure/literal/Test.long_num:J
  #14 = Fieldref           #19.#59        // com/pioneerzcy/pure/literal/Test.boolean_flag:Z
  #15 = String             #60            // b_test_part
  #16 = Long               5555l
  #18 = Fieldref           #19.#61        // com/pioneerzcy/pure/literal/Test.long_delay_num:J
  #19 = Class              #62            // com/pioneerzcy/pure/literal/Test
  #20 = Class              #63            // java/lang/Object
  #21 = Utf8               int_num
  #22 = Utf8               I
  #23 = Utf8               byte_num
  #24 = Utf8               B
  #25 = Utf8               short_num
  #26 = Utf8               S
  #27 = Utf8               char_num
  #28 = Utf8               C
  #29 = Utf8               float_num
  #30 = Utf8               F
  #31 = Utf8               double_num
  #32 = Utf8               D
  #33 = Utf8               long_num
  #34 = Utf8               J
  #35 = Utf8               boolean_flag
  #36 = Utf8               Z
  #37 = Utf8               long_delay_num
  #38 = Utf8               <init>
  #39 = Utf8               ()V
  #40 = Utf8               Code
  #41 = Utf8               LineNumberTable
  #42 = Utf8               LocalVariableTable
  #43 = Utf8               this
  #44 = Utf8               Lcom/pioneerzcy/pure/literal/Test;
  #45 = Utf8               test
  #46 = Utf8               a_test
  #47 = Utf8               b_test
  #48 = Utf8               Ljava/lang/String;
  #49 = Utf8               SourceFile
  #50 = Utf8               Test.java
  #51 = NameAndType        #38:#39        // "<init>":()V
  #52 = NameAndType        #21:#22        // int_num:I
  #53 = NameAndType        #23:#24        // byte_num:B
  #54 = NameAndType        #25:#26        // short_num:S
  #55 = NameAndType        #27:#28        // char_num:C
  #56 = NameAndType        #29:#30        // float_num:F
  #57 = NameAndType        #31:#32        // double_num:D
  #58 = NameAndType        #33:#34        // long_num:J
  #59 = NameAndType        #35:#36        // boolean_flag:Z
  #60 = Utf8               b_test_part
  #61 = NameAndType        #37:#34        // long_delay_num:J
  #62 = Utf8               com/pioneerzcy/pure/literal/Test
  #63 = Utf8               java/lang/Object
{
  public com.pioneerzcy.pure.literal.Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        12
         7: putfield      #2                  // Field int_num:I
        10: aload_0
        11: bipush        121
        13: putfield      #3                  // Field byte_num:B
        16: aload_0
        17: bipush        30
        19: putfield      #4                  // Field short_num:S
        22: aload_0
        23: bipush        97
        25: putfield      #5                  // Field char_num:C
        28: aload_0
        29: ldc           #6                  // float 45.3f
        31: putfield      #7                  // Field float_num:F
        34: aload_0
        35: ldc2_w        #8                  // double 39.8d
        38: putfield      #10                 // Field double_num:D
        41: aload_0
        42: ldc2_w        #11                 // long 2323l
        45: putfield      #13                 // Field long_num:J
        48: aload_0
        49: iconst_1
        50: putfield      #14                 // Field boolean_flag:Z
        53: return
      LineNumberTable:
        line 8: 0
        line 10: 4
        line 11: 10
        line 12: 16
        line 13: 22
        line 14: 28
        line 15: 34
        line 16: 41
        line 17: 48
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      54     0  this   Lcom/pioneerzcy/pure/literal/Test;

  public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: bipush        123
         2: istore_1
         3: ldc           #15                 // String b_test_part
         5: astore_2
         6: aload_0
         7: ldc2_w        #16                 // long 5555l
        10: putfield      #18                 // Field long_delay_num:J
        13: return
      LineNumberTable:
        line 21: 0
        line 22: 3
        line 23: 6
        line 24: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  this   Lcom/pioneerzcy/pure/literal/Test;
            3      11     1 a_test   I
            6       8     2 b_test   Ljava/lang/String;
}
SourceFile: "Test.java"
原文  http://tech.dianwoda.com/2020/01/02/javalei-wen-jian-jie-gou/
正文到此结束
Loading...