Java[虚拟机]-Java Class文件结构学习总结

高效学习博客:
Class类文件结构: https://www.cnblogs.com/wade-luffy/p/5929325.html
Java Class文件结构解析 及 实例分析验证 : https://blog.csdn.net/tjiyu/article/details/53870153
Class文件中的常量池详解(上): https://blog.csdn.net/wangtaomtk/article/details/52267548
官方java class文件结构文档:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
可以下载个010Editor,打开class文件后会提示下载解析class文件脚本,之后就很方便,每一项都清清楚楚是干嘛的。
http://www.sweetscape.com/download/010editor/

一.Class文件整体结构:

全在这张图中了,再结合后面解析二进制文件字节码的例子,基本就掌握了大概的结构规则,当然这只是简单的解析。

在这里插入图片描述

本图的draw.io源文件在百度盘中下载,可再自行添加修改:
链接: https://pan.baidu.com/s/1FMPUeBGaCKFZXEh-M8a6vA 提取码: nhwf

Class文件中只有两类数据类型:

  1. 无符号数:
    无符号数属于基本的数据类型,以u1、u2、u4、u8来表示一个字节、两个字节...的无符号数;
    无符号数用来描述数字、索引引用、数量值或UTF-8编码构成的字符串值。
  2. 表:
    表是由多个无符号数或其他表作为数据项构成的复合数据类型,一般以"_info"结尾
    表中的项长度不固定

主要结构类型:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

讲解:

类型 名称 数量 说明
u4 magic 1 魔数:确定一个文件是否是Class文件
u2 minor_version 1 Class文件的次版本号
u2 major_version 1 Class文件的主版本号:一个JVM实例只能支持特定范围内版本号的Class文件(可以向下兼容)。
u2 constant_pool_count 1 常量表数量
cp_info constant_pool constant_pool_count-1 常量池:以理解为Class文件的资源仓库,后面的其他数据项可以引用常量池内容
u2 access_flags 1 类的访问标志信息:用于表示这个类或者接口的访问权限及基础属性。
u2 this_class 1 指向当前类的常量索引:用来确定这个类的全限定名。
u2 super_class 1 指向当父类的常量索引:用来确定这个类的父类的全限定名。
u2 interfaces_count 1 接口的数量
u2 interfaces interfaces_count 指向接口的常量索引:用来描述这个类实现了哪些接口。
u2 fields_count 1 字段表数量
field_info fields fields_count 字段表集合:描述当前类或接口声明的所有字段。
u2 methods_count 1 方法表数量
method_info methods methods_count 方法表集合:只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。
u2 attributes_count 1 属性表数量
attributes_info attributes attributes_count 属性表集合:用于描述某些场景专有的信息,如字节码的指令信息等等。

二.举个例子

1. 如下class文件:**

/home/chengang/IdeaProjects/JavaTest/src/Computer.java
public class Computer {
    final String TAG = "Log";
    private String mName;
    int mPrice;

    public Computer(String mModel, int mPrice) {
        this.mName = mModel;
        this.mPrice = mPrice;
        System.out.println(TAG + " new Computer()");
    }

    public String getName() {
        return mName;
    }

    public int getPrice() {
        return mPrice;
    }
}

2.生成如下字节码:**

用vim打开文件并二进制查看:
vim /home/chengang/IdeaProjects/JavaTest/out/production/JavaTest/Computer.class
:%!xxd


在这里插入图片描述

具体可参考:

00000000: cafe babe (魔数u4) 0000 0034(版本号u2+u2) 0032(cp_count,常量池50个,包含一个索引0的) 0a(tag:0a=10 是Methodref)00 0a(class_index:0a=10,常量池中第10项)00 1f(name_and_type_index:001f=31,常量池中第31项)08(tag:08=8 是String)  .......4.2......
00000010: 0020(string_index:0020=32,常量池第32项“Log”) 09(tag:09=9 是Fieldref)00 07(class_index:07=7,常量池中第7项,“Computer”)00 21(name_type_index:0021=33,常量池中第33项,“TAG:Ljava/lang/String;”,之后类似)09 0007 0022 0900 0700  . ....!...."....
00000020: 2309 0024 0025 0700 2608 0027 0a00 2800  #..$.%..&..'..(.
00000030: 2907 002a 0100 0354 4147 0100 124c 6a61  )..*...TAG...Lja
00000040: 7661 2f6c 616e 672f 5374 7269 6e67 3b01  va/lang/String;.
00000050: 000d 436f 6e73 7461 6e74 5661 6c75 6501  ..ConstantValue.
00000060: 0005 6d4e 616d 6501 0006 6d50 7269 6365  ..mName...mPrice
00000070: 0100 0149 0100 063c 696e 6974 3e01 0016  ...I......
00000080: 284c 6a61 7661 2f6c 616e 672f 5374 7269  (Ljava/lang/Stri
00000090: 6e67 3b49 2956 0100 0443 6f64 6501 000f  ng;I)V...Code...
000000a0: 4c69 6e65 4e75 6d62 6572 5461 626c 6501  LineNumberTable.
000000b0: 0012 4c6f 6361 6c56 6172 6961 626c 6554  ..LocalVariableT
000000c0: 6162 6c65 0100 0474 6869 7301 000a 4c43  able...this...LC
000000d0: 6f6d 7075 7465 723b 0100 066d 4d6f 6465  omputer;...mMode
000000e0: 6c01 0007 6765 744e 616d 6501 0014 2829  l...getName...()
000000f0: 4c6a 6176 612f 6c61 6e67 2f53 7472 696e  Ljava/lang/Strin
00000100: 673b 0100 0867 6574 5072 6963 6501 0003  g;...getPrice...
00000110: 2829 4901 000a 536f 7572 6365 4669 6c65  ()I...SourceFile
00000120: 0100 0d43 6f6d 7075 7465 722e 6a61 7661  ...Computer.java
00000130: 0c00 1100 2b01 0003 4c6f 670c 000b 000c  ....+...Log.....
00000140: 0c00 0e00 0c0c 000f 0010 0700 2c0c 002d  ............,..-
00000150: 002e 0100 0843 6f6d 7075 7465 7201 0012  .....Computer...
00000160: 4c6f 6720 6e65 7720 436f 6d70 7574 6572  Log new Computer
00000170: 2829 0700 2f0c 0030 0031 0100 106a 6176  ()../..0.1...jav
00000180: 612f 6c61 6e67 2f4f 626a 6563 7401 0003  a/lang/Object...
00000190: 2829 5601 0010 6a61 7661 2f6c 616e 672f  ()V...java/lang/
000001a0: 5379 7374 656d 0100 036f 7574 0100 154c  System...out...L
000001b0: 6a61 7661 2f69 6f2f 5072 696e 7453 7472  java/io/PrintStr
000001c0: 6561 6d3b 0100 136a 6176 612f 696f 2f50  eam;...java/io/P
000001d0: 7269 6e74 5374 7265 616d 0100 0770 7269  rintStream...pri
000001e0: 6e74 6c6e 0100 1528 4c6a 6176 612f 6c61  ntln...(Ljava/la
000001f0: 6e67 2f53 7472 696e 673b 2956 (在这之前为常量池)0021(类access_flag:0021=0x0020&0x001,查表代表ACC_SUPER和ACC_PUBLIC) 0007(this_class:0007=7,常量池第7项,“Computer”)  ng/String;)V.!..
00000200: 000a(super_class:000a=10,常量池第10项,“java/lang/Object”,java中所有基类都默认有一个父类Object类) 0000(interfaces_count:0000=0,无接口,因此无后面的interface部分) 0003(fields_count:0003=3,代表有3个字段,之后有三个field_info,字段表从此开始) 0010(字段access_flag:0x0010查字段的访问标志表为ACC_FINAL类型,代表着final修饰符) 000b(name_index:000b=11,常量池第11项,“TAG”) 000c(descriptor_index:000c=12,字段描述符,常量池第12项,“Ljava/lang/String;”) 0001(attributes_count:0001=1,后面有一个属性项) 000d(attribute_name_index:000d=13,常量池第13项,“ConstantValue”)  ................
00000210: 0000 0002(u4 attribute_length:2) 0002(constantvalue_index:0002=2,常量池第2项,“Log”) 0002(第2个字段的access_flag:0002=2,0x0002查表为ACC_PRIVATE,即private修饰符) 000e(name_index:000e=14,常量池第14项,“mName”) 000c(descriptor_index:000c=12,常量池第12项,“Ljava/lang/String;”) 0000(attributes_count:0000=0,后面没有属性项) 0000(第三个字段的access_flag,没有使用需要设置为0)  ................
00000220: 000f(name_index:000f=15,常量池第15项,“mPrice”) 0010(descriptor_index:0010=16,常量池项中的第16项,“I”代表Int类型) 0000(attributes_count:0,后面无属性项,字段表至此结束) 0003(methods_count=3代表后面有三个method_info,方法表从此处开始) 0001(方法的access_flag:0001=0x0001,查询方法项的access_flag为ACC_PUBLIC) 0011(name_index:0011=17,常量池中的第17项“”,代表着实例构造器,如果代码中有定义构造函数,在解析的语法分析阶段被重命名为()) 0012(descriptor_index:0012=18,常量池中的第18项,“(Ljava/lang/String;I)V”,代表着void类型的方法,有String和int两个类型的参数) 0001(attribute_count:0001=1,后面有一个属性项)  ................
00000230: 0013(attribute_name_index:0013=19,常量表中的第19项,“Code”,code属性,再根据code属性的组成格式,依次解析后面的每一个字节的含义) 0000 006f 0002 0003 0000 001d 2ab7  .....o........*.
00000240: 0001 2a12 02b5 0003 2a2b b500 042a 1cb5  ..*.....*+...*..
00000250: 0005 b200 0612 08b6 0009 b100 0000 0200  ................
00000260: 1400 0000 1a00 0600 0000 0600 0400 0200  ................
00000270: 0a00 0700 0f00 0800 1400 0900 1c00 0a00  ................
00000280: 1500 0000 2000 0300 0000 1d00 1600 1700  .... ...........
00000290: 0000 0000 1d00 1800 0c00 0100 0000 1d00  ................
000002a0: 0f00 1000 0200 0100 1900 1a00 0100 1300  ................
000002b0: 0000 2f00 0100 0100 0000 052a b400 04b0  ../........*....
000002c0: 0000 0002 0014 0000 0006 0001 0000 000d  ................
000002d0: 0015 0000 000c 0001 0000 0005 0016 0017  ................
000002e0: 0000 0001 001b 001c 0001 0013 0000 002f  .............../
000002f0: 0001 0001 0000 0005 2ab4 0005 ac00 0000  ........*.......
00000300: 0200 1400 0000 0600 0100 0000 1100 1500  ................
00000310: 0000 0c00 0100 0000 0500 1600 1700 0000  ................
00000320: 0100 1d00 0000 0200 1e                   .........

可以使用010Editor打开class文件,更加清晰明了:


在这里插入图片描述

3.Javap反解析结构图**

javap反编译工具,可以根据class字节码反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息:

chengang@mi:~$ javap  -v '/home/chengang/IdeaProjects/JavaTest/out/production/JavaTest/Computer.class' 
Classfile /home/chengang/IdeaProjects/JavaTEst/out/production/JavaTest/Computer.class
  Last modified Apr 26, 2019; size 809 bytes
  MD5 checksum 561774586f9e1d34a2e35a7617d17f5c
  Compiled from "Computer.java"
public class Computer
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#31        // java/lang/Object."":()V
   #2 = String             #32            // Log
   #3 = Fieldref           #7.#33         // Computer.TAG:Ljava/lang/String;
   #4 = Fieldref           #7.#34         // Computer.mName:Ljava/lang/String;
   #5 = Fieldref           #7.#35         // Computer.mPrice:I
   #6 = Fieldref           #36.#37        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Class              #38            // Computer
   #8 = String             #39            // Log new Computer()
   #9 = Methodref          #40.#41        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #10 = Class              #42            // java/lang/Object
  #11 = Utf8               TAG
  #12 = Utf8               Ljava/lang/String;
  #13 = Utf8               ConstantValue
  #14 = Utf8               mName
  #15 = Utf8               mPrice
  #16 = Utf8               I
  #17 = Utf8               
  #18 = Utf8               (Ljava/lang/String;I)V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               LComputer;
  #24 = Utf8               mModel
  #25 = Utf8               getName
  #26 = Utf8               ()Ljava/lang/String;
  #27 = Utf8               getPrice
  #28 = Utf8               ()I
  #29 = Utf8               SourceFile
  #30 = Utf8               Computer.java
  #31 = NameAndType        #17:#43        // "":()V
  #32 = Utf8               Log
  #33 = NameAndType        #11:#12        // TAG:Ljava/lang/String;
  #34 = NameAndType        #14:#12        // mName:Ljava/lang/String;
  #35 = NameAndType        #15:#16        // mPrice:I
  #36 = Class              #44            // java/lang/System
  #37 = NameAndType        #45:#46        // out:Ljava/io/PrintStream;
  #38 = Utf8               Computer
  #39 = Utf8               Log new Computer()
  #40 = Class              #47            // java/io/PrintStream
  #41 = NameAndType        #48:#49        // println:(Ljava/lang/String;)V
  #42 = Utf8               java/lang/Object
  #43 = Utf8               ()V
  #44 = Utf8               java/lang/System
  #45 = Utf8               out
  #46 = Utf8               Ljava/io/PrintStream;
  #47 = Utf8               java/io/PrintStream
  #48 = Utf8               println
  #49 = Utf8               (Ljava/lang/String;)V
{
  final java.lang.String TAG;
    descriptor: Ljava/lang/String;
    flags: ACC_FINAL
    ConstantValue: String Log

  int mPrice;
    descriptor: I
    flags:

  public Computer(java.lang.String, int);
    descriptor: (Ljava/lang/String;I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: aload_0
         5: ldc           #2                  // String Log
         7: putfield      #3                  // Field TAG:Ljava/lang/String;
        10: aload_0
        11: aload_1
        12: putfield      #4                  // Field mName:Ljava/lang/String;
        15: aload_0
        16: iload_2
        17: putfield      #5                  // Field mPrice:I
        20: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        23: ldc           #8                  // String Log new Computer()
        25: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 6: 0
        line 2: 4
        line 7: 10
        line 8: 15
        line 9: 20
        line 10: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  this   LComputer;
            0      29     1 mModel   Ljava/lang/String;
            0      29     2 mPrice   I

  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #4                  // Field mName:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LComputer;

  public int getPrice();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #5                  // Field mPrice:I
         4: ireturn
      LineNumberTable:
        line 17: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LComputer;
}
SourceFile: "Computer.java"

三.Class文件结构表项详细及重点

字段描述符中基本类型:

字符 类型 含义
B byte 有符号字节型数
C char Unicode 字符,UTF-16 编码
D double 双精度浮点数
F float 单精度浮点数
I int 整型数
J long 长整数
S short 有符号短整数
Z boolean 布尔值:true/false
L Classname; reference 一个名为的实例
[ reference 一个一维数组
V void void返回值(其实不属于基本类型,而是VoidDescriptor)

1.魔数

Class文件开始是4个字节定义为魔数(Magic Number);

唯一作用:确定一个文件是否是Class文件

2.Class文件版本

魔数之后的4个字节标识版本号,即minor_version为0,major_version为52(0034对应的十进制),版本为52.0(符合JDK8)
各个版本的对应关系:

JDK版本号 Class版本号 16进制
1.1 45.0 00 00 00 2D
1.2 46.0 00 00 00 2E
1.3 47.0 00 00 00 2F
1.4 48.0 00 00 00 30
1.5 49.0 00 00 00 31
1.6 50.0 00 00 00 32
1.7 51.0 00 00 00 33
1.8 52.0 00 00 00 34

3.常量池

常量池可以理解为Class文件的资源仓库,后面的其他数据项可以引用常量池内容;
常量池每一项都是一个表包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量;
主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References);

常量池里的没一项都有如下格式:

//常量池项的通用格式
cp_info {
    u1 tag;// 1byte 标志位,标识了info[]项的内容,即 info[]属于什么类型
    u1 info[];
}

JDK 1.8 有14中常量类型及各自组成格式:

Constant Type Value 占用字节数 组成部分 comments
CONSTANT_Class 7 3 u1 tag;
u2 name_index;
CONSTANT_Fieldref 9 5 u1 tag;
u2 class_index;
u2 name_and_type_index;
CONSTANT_Methodref 10 5 u1 tag;
u2 class_index;
u2 name_and_type_index;
CONSTANT_InterfaceMethodref 11 5 u1 tag;
u2 class_index;
u2 name_and_type_index;`
CONSTANT_String 8 3 u1 tag;
u2 string_index;
CONSTANT_Integer 3 5 u1 tag;
u4 bytes
CONSTANT_Float 4 5 u1 tag;
u4 bytes;`
CONSTANT_Long 5 9 u1 tag;
u4 high_bytes;
u4 low_bytes;
占用2个常量表项空间
CONSTANT_Double 6 9 u1 tag;
u4 high_bytes;
u4 low_bytes;
占用2个常量表项空间
CONSTANT_NameAndType 12 5 u1 tag;
u2 name_index;
u2 descriptor_index;
CONSTANT_Utf8 1 1+2+length u1 tag;
u2 length;
u1 bytes[length];
CONSTANT_MethodHandle 15 4 u1 tag;
u1 reference_kind;
u2 reference_index;
CONSTANT_MethodType 16 3 u1 tag;
u2 descriptor_index;
CONSTANT_InvokeDynamic 18 5 u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;

4.类的访问标志

用于表示这个类或者接口的访问权限及基础属性;

可以用16位掩码标志,目前只使用8种(JDK1.5增加后面三种),没使用的需要设置为0;

类的access_flags表:

标记名 含义
ACC_PUBLIC 0x0001 可以被包的类外访问。
ACC_FINAL 0x0010 不允许有子类。
ACC_SUPER 0x0020 当用到 invokespecial 指令时,需要特殊处理的父类方法。
ACC_INTERFACE 0x0200 标识定义的是接口而不是类。
ACC_ABSTRACT 0x0400 不能被实例化。
ACC_SYNTHETIC 0x1000 标识并非 Java 源码生成的代码。
ACC_ANNOTATION 0x2000 标识注解类型
ACC_ENUM 0x4000 标识枚举类型

5.当前类索引

u2
指向当前类的在常量池项中的该类的索引

6.父类索引

u2
指向当前类的父类在常量池项中的该类的索引
用来确定这个类的父类的全限定名;
因为Java语言不允许多生继承,所以父类索引只有一个;并且除java.lang.Object外,其他所有类的父类索引都不能为0;
对于接口来说,索引指向的项必须为代表java.lang.Object的CONSTANT_Class_info类型常量;

7.接口索引集合

size 项目
u2 interfaces_count
u2 interfaces

implements(接口extends)后实现的按顺序从左到右排列在接口索引集合;
如果没有实现任何接口,interfaces_count为0,后面不再有interfaces[interfaces_count];

8.字段表 Fields

字段表集合描述当前类或接口声明的所有字段;

field_info用于表示当前类或接口中某个字段的完整描述,包括类字段(static字段)或实例字段,不包括局部变量,但不包括从父类或父接口继承的部分字段;

对内部类,编译器可能自动添加对外部类实例的字段;

基本组成:

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

access_flag:
是用于定义字段被访问权限和基础属性的掩码标志;
和前面类的访问标志一样,没使用的需要设置为0;
有些标记是互斥的,如不能同时设置标志 ACC_FINAL 和 ACC_VOLATILE;
字段的access_flag表:

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.

attribute_info在后面详细展开

9.方法表

methods[]数组只描述当前类或接口中声明的方法;
包括实例方法、类方法、实例初始化方法方法和类或接口初始化方法方法;但不包括从父类或父接口继承的方法;
可能存在编译自动添加的方法,如类构造器""和实例构造器"";
method_info用于表示当前类或接口中某个方法的完整描述;
如attributes[]中可能存在"Code"属性,表示方法逻辑代码编译后的字节码指令;

method_info {
    u2             access_flags;//用于定义当前方法的访问权限和基本属性的掩码标志;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

access_flags:
和前面类的访问标志一样,没使用的需要设置为0;
有些标记是互斥的,如不能同时设置标志 ACC_FINAL 和 ACC_ABSTRACT;
方法项的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.

attribute_info在后面详细展开

10.属性表

在Class文件的 ClassFile结构、字段表、方法表中都可以存储放自己的属性表集合,所以并不像最前面那Class文件结构那么直观,即属性不都是放在Class文件的最后;
属性表的通用格式:

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

不同属性的info是不同的,即attribute_name_index对应的类型有特定的属性格式
具体在 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.2 中,不多赘述。

你可能感兴趣的:(Java[虚拟机]-Java Class文件结构学习总结)