JVM实战与原理---Class文件结构

JVM实战与原理


目录

Class文件结构

1. 数据结构

2. class文件结构详解

2.1 魔数与Class文件的版本

2.2 常量池

2.3 访问标志

2.4 类索引、父类索引与接口索引集合

2.5 字段表集合

2.6 方法表集合

2.7 属性表集合


Class文件结构

章节目的:介绍class文件的结构


引言:当使用javac命令对java文件进行编译后,便生成了Class文件,那么Class文件的结构是怎么样的呢?

下面是Class文件结构的介绍。

1. 数据结构

首先,我们需要知道Class文件的数据结构如下,其中u1、u2、u4分别代表1个字节、2个字节、4个字节,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];
}

2. class文件结构详解

接着,我们继续通过以下这段java代码来分析

class Person {
	
	private static String name = "wenxl";
	
	public static void main(String[] args) {
		System.out.println(name);
	}
}

对于已经编译生成了class文件,此时将class文件通过winHex打开,会有如下十六进制的字节码,例如首个CA就为一个字节,即为u1。

Offset      0  1  2  3  4  5  6  7   8  9 10 11 12 13 14 15

00000000   CA FE BA BE 00 00 00 34  00 22 0A 00 07 00 13 09   漱壕   4 "      
00000016   00 14 00 15 09 00 06 00  16 0A 00 17 00 18 08 00                   
00000032   19 07 00 1A 07 00 1B 01  00 04 6E 61 6D 65 01 00             name  
00000048   12 4C 6A 61 76 61 2F 6C  61 6E 67 2F 53 74 72 69    Ljava/lang/Stri
00000064   6E 67 3B 01 00 06 3C 69  6E 69 74 3E 01 00 03 28   ng;   <init>   (
00000080   29 56 01 00 04 43 6F 64  65 01 00 0F 4C 69 6E 65   )V   Code   Line
00000096   4E 75 6D 62 65 72 54 61  62 6C 65 01 00 04 6D 61   NumberTable   ma
00000112   69 6E 01 00 16 28 5B 4C  6A 61 76 61 2F 6C 61 6E   in   ([Ljava/lan
00000128   67 2F 53 74 72 69 6E 67  3B 29 56 01 00 08 3C 63   g/String;)V   <c
00000144   6C 69 6E 69 74 3E 01 00  0A 53 6F 75 72 63 65 46   linit>   SourceF
00000160   69 6C 65 01 00 0B 50 65  72 73 6F 6E 2E 6A 61 76   ile   Person.jav
00000176   61 0C 00 0A 00 0B 07 00  1C 0C 00 1D 00 1E 0C 00   a               
00000192   08 00 09 07 00 1F 0C 00  20 00 21 01 00 05 77 65             !   we
00000208   6E 78 6C 01 00 06 50 65  72 73 6F 6E 01 00 10 6A   nxl   Person   j
00000224   61 76 61 2F 6C 61 6E 67  2F 4F 62 6A 65 63 74 01   ava/lang/Object 
00000240   00 10 6A 61 76 61 2F 6C  61 6E 67 2F 53 79 73 74     java/lang/Syst
00000256   65 6D 01 00 03 6F 75 74  01 00 15 4C 6A 61 76 61   em   out   Ljava
00000272   2F 69 6F 2F 50 72 69 6E  74 53 74 72 65 61 6D 3B   /io/PrintStream;
00000288   01 00 13 6A 61 76 61 2F  69 6F 2F 50 72 69 6E 74      java/io/Print
00000304   53 74 72 65 61 6D 01 00  07 70 72 69 6E 74 6C 6E   Stream   println
00000320   01 00 15 28 4C 6A 61 76  61 2F 6C 61 6E 67 2F 53      (Ljava/lang/S
00000336   74 72 69 6E 67 3B 29 56  00 20 00 06 00 07 00 00   tring;)V        
00000352   00 01 00 0A 00 08 00 09  00 00 00 03 00 00 00 0A                   
00000368   00 0B 00 01 00 0C 00 00  00 1D 00 01 00 01 00 00                   
00000384   00 05 2A B7 00 01 B1 00  00 00 01 00 0D 00 00 00     *? ?        
00000400   06 00 01 00 00 00 01 00  09 00 0E 00 0F 00 01 00                   
00000416   0C 00 00 00 26 00 02 00  01 00 00 00 0A B2 00 02       &        ? 
00000432   B2 00 03 B6 00 04 B1 00  00 00 01 00 0D 00 00 00   ? ? ?        
00000448   0A 00 02 00 00 00 06 00  09 00 07 00 08 00 10 00                   
00000464   0B 00 01 00 0C 00 00 00  1E 00 01 00 00 00 00 00                   
00000480   06 12 05 B3 00 03 B1 00  00 00 01 00 0D 00 00 00      ? ?        
00000496   06 00 01 00 00 00 03 00  01 00 11 00 00 00 02 00                   
00000512   12                                                  

2.1 魔数与Class文件的版本

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    .......
}

对应的字节码为:CA FE BA BE 00 00 00 34

作用:每个Class文件的头4个字节称为魔数magic,作用是确定这个文件是否为JVM能接受的Clcass文件,固定为0xCAFEBABE。

后4字节说明Class文件的版本

字节码详解:00 00 00 34 说明该文件版本为JDK1.8。

2.2 常量池

ClassFile {
    .......
    u2 constant_pool_count; 
    cp_info constant_pool[constant_pool_count-1];
    .......
}

对应的字节码为:00 22

作用:前两个字节说明常量池的数量,后面则是常量池内容。常量池可以理解是Class文件的资源仓库。

字节码详解:其中前两个字节0x0022转换为十进制为34,说明有33个常量

cp_info常量类型的数据结构为

cp_info {
    u1 tag; 
    u1 info[];
}

其中tag的值代表当前常量属于那种常量类型

Tag Value

Constant Type

DESCDATA STRUCTURE
1CONSTANT_Utf8utf-8编码的字符串CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}
3CONSTANT_Integer整形CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}
4CONSTANT_Float浮点型CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}
5CONSTANT_Long长整形CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
6CONSTANT_Double双精度浮点型CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
7CONSTANT_Class类或接口的符号引用CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}
8CONSTANT_String字符串类型CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}
9CONSTANT_Fieldref字段的符号引用CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
10CONSTANT_Methodref类中方法的符号引用CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
11CONSTANT_InterfaceMethodref接口中方法的符号引用CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
12CONSTANT_NameAndType字段或方法的部分符号引用CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}
15CONSTANT_MethodHandle表示方法句柄CONSTANT_MethodHandle_info {
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}
16CONSTANT_MethodType标识方法类型CONSTANT_MethodType_info {
    u1 tag;
    u2 descriptor_index;
}
18CONSTANT_InvokeDynamic表示一个动态方法调用点CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}

我们按字节顺序开始分析,第一个tag值为0x0A,对应常量类型为CONSTANT_Methodref,其结构为

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

对应的字节码为

0A00 0700 13

0007为class_index指向一个CONSTANT_Class_info

0013为name_and_type_index指向一个CONSTANT_NameAndType

那指向的这两个又是什么呢?我们往下继续分析

第二个tag值为0x09,对应常量类型为CONSTANT_Fieldref,其结构为

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

对应的字节码为

0900 1400 15

0014为class_index指向一个CONSTANT_Class_info

0015为name_and_type_index指向一个CONSTANT_NameAndType

我们按这样的逻辑将常量池字 节码解析为

IndexTagInfoTag Value
10A0007 0013CONSTANT_Methodref
2090014 0015CONSTANT_Fieldref
3090006 0016CONSTANT_Fieldref
40A0017 0018CONSTANT_Methodref
5080019CONSTANT_String
607001ACONSTANT_Class
707001BCONSTANT_Class
8010004 6E6D 6561CONSTANT_Utf8
9010012 4C.. ..3BCONSTANT_Utf8
10010006 3C.. ..3ECONSTANT_Utf8
11010003 2829 56CONSTANT_Utf8
12010004 436F 6465CONSTANT_Utf8
1301000F 4C.. ..65CONSTANT_Utf8
14010004 6D61 696ECONSTANT_Utf8
15010016 28.. ..56CONSTANT_Utf8
16010008 3C.. ..3ECONSTANT_Utf8
1701000A 53.. ..65CONSTANT_Utf8
1801000B 50.. ..61CONSTANT_Utf8
190C000A 000BCONSTANT_NameAndType
2007001CCONSTANT_Class
210C001D 001ECONSTANT_NameAndType
220C0008 0009CONSTANT_NameAndType
2307001FCONSTANT_Class
240C0020 0021CONSTANT_NameAndType
2501 0005 77.. ..6CCONSTANT_Utf8
2601 0006 50.. ..6ECONSTANT_Utf8
2701 0010 6A.. ..74CONSTANT_Utf8
2801 0010 6A.. ..6DCONSTANT_Utf8
2901 0003 6F75 74CONSTANT_Utf8
3001 0015 4C.. ..3BCONSTANT_Utf8
3101 0013 6A.. ..6DCONSTANT_Utf8
3201 0007 70.. ..6ECONSTANT_Utf8
3301 0015 28.. ..56CONSTANT_Utf8

这时我们再回到第一个CONSTANT_Methodref,该常量用来描述的方法,其中

0007为class_index指向一个CONSTANT_Class_info,说明是哪个类的方法

0013为name_and_type_index指向一个CONSTANT_NameAndType,说明的是这个方法的方法名与方法的入参及返回值类型

0007即index为7的常量,我们可以看到是

707001BCONSTANT_Class

CONSTANT_Class的结构为

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

同理,index为001B的即第27个常量是

2701 0010 6A.. ..74CONSTANT_Utf8

CONSTANT_Utf8的结构为

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

length为0010,即后面跟着长度为16的字符串,6A.. ..74转换为对应的字符串为java/lang/Object,说明调用的是java/lang/Object的方法

同样,0013指向第19个常量,是个CONSTANT_NameAndType_info

190C000A 000BCONSTANT_NameAndType

结构如下

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

000A指向第10个常量,转换为对应的字符串为<init>,说明方法名为<init>

10010006 3C.. ..3ECONSTANT_Utf8

000B指向第11个常量,转换为对应的字符串为()V,说明入参为空,返回值类型是void。

 

我们可以用javap -verbose Person的命令反编译,就可以得到javap工具帮我们解析的字节码描述,我们发现其中Constant pool部分与我们分析出的结果是一致的。

Classfile /D:/用户目录/我的文档/Code/JVMSearch/Person.class
  Last modified 2020-12-19; size 513 bytes
  MD5 checksum dea507ac0ecc8c9063c778c6f748a2bb
  Compiled from "Person.java"
class Person
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#19         // java/lang/Object."<init>":()V
   #2 = Fieldref           #20.#21        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Fieldref           #6.#22         // Person.name:Ljava/lang/String;
   #4 = Methodref          #23.#24        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #25            // wenxl
   #6 = Class              #26            // Person
   #7 = Class              #27            // java/lang/Object
   #8 = Utf8               name
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               <clinit>
  #17 = Utf8               SourceFile
  #18 = Utf8               Person.java
  #19 = NameAndType        #10:#11        // "<init>":()V
  #20 = Class              #28            // java/lang/System
  #21 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #22 = NameAndType        #8:#9          // name:Ljava/lang/String;
  #23 = Class              #31            // java/io/PrintStream
  #24 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #25 = Utf8               wenxl
  #26 = Utf8               Person
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  Person();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: getstatic     #3                  // Field name:Ljava/lang/String;
         6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         9: return
      LineNumberTable:
        line 6: 0
        line 7: 9

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #5                  // String wenxl
         2: putstatic     #3                  // Field name:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 3: 0
}
SourceFile: "Person.java"

2.3 访问标志

ClassFile {
    .......
    u2             access_flags;
    .......
}

对应的字节码为:00 20

作用:用于识别类或者接口层次的访问信息

字节码详解:每个位所代表的意义如下表,因为该类是JDK1.2之后的编译器进行编译,且其他标识均为0,故为00 20。大家可以试试在类被声明为public,abstract,final的情况下,字节码是如何展示的。

Flag NameValueInterpretation
ACC_PUBLIC0x0001是否为public类型
ACC_FINAL0x0010是否被声明为final,只有类可设置
ACC_SUPER0x0020是否允许使用invokespecial字节码指令的新语意,JDK1.0.2该指令语意发生改变,故JDK1.0.2后该标志都为真
ACC_INTERFACE0x0200标识这是一个接口
ACC_ABSTRACT0x0400是否为abstract类型
ACC_SYNTHETIC0x1000标识这个类并非由用户代码产生
ACC_ANNOTATION0x2000标识这是一个注解
ACC_ENUM0x4000标识这是一个枚举

2.4 类索引、父类索引与接口索引集合

ClassFile {
    .......
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    .......
}

对应的字节码为:00 06 00 07 00 00

作用:这三项数据确定了这个类的继承关系,类索引(this_class)确定类的全限定名,父类索引(super_class)确定类的父类的全限定名,接口索引集合(interfaces)描述类实现了哪些接口

字节码详解:00 06指向常量池第6个常量,类型为CONSTACT_Class_info,值为Person。

00 07同样类型为CONSTACT_Class_info,值为java/lang/Object。

00 00说明该类没实现任何接口。好奇的同学可以试试实现了接口后,这里的字节码是如何展示的。

2.5 字段表集合

ClassFile {
    .......
    u2             fields_count;
    field_info     fields[fields_count];
    .......
}

对应的字节码为:00 01 00 0A 00 08 00 09 00 00

作用:描述接口或者类中的声明的变量。

字节码详解:

其中前两个字节0x0001转换为十进制为1,说明有1个字段。

field_info常量类型的数据结构为

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

access_flag为字段访问标志,含义表如下。字节码为00 0A,说明ACC_PRIVATE和ACC_STATIC为真,即该字段为private和static

Flag NameValueInterpretation
ACC_PUBLIC0x0001字段是否public
ACC_PRIVATE0x0002字段是否private
ACC_PROTECTED0x0004字段是否protected
ACC_STATIC0x0008字段是否static
ACC_FINAL0x0010字段是否final
ACC_VOLATILE0x0040字段是否volatile
ACC_TRANSIENT0x0080字段是否transient
ACC_SYNTHETIC0x1000字段是否由编译器自动产生的
ACC_ENUM0x4000字段是否enum

name_index代表字段的简单名称,descriptor_index代表字段和方法的描述符。

字节码为00 08 00 09,指向常量池第8和第9个常量,类型为CONSTACT_Utf8_info,值分别为name和Ljava/lang/String;。

说明该字段名为name,描述符为java/lang/String。

attribute_info则是该字段的属性表,字节码为00 00, 说明没有属性。

2.6 方法表集合

ClassFile {
    .......
    u2             methods_count;
    method_info    methods[methods_count];
    .......
}

对应的字节码为:00 03 00 00 00 0A 00 0B 00 01

作用:描述接口或者类中的方法。

字节码详解:

其中前两个字节0x0003转换为十进制为3,说明有3个方法。

method_info常量类型的数据结构为

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

access_flag为字段访问标志,含义表如下。字节码为00 00,说明标志均为假

Flag NameValueInterpretation
ACC_PUBLIC0x0001方法是否public
ACC_PRIVATE0x0002方法是否private
ACC_PROTECTED0x0004方法是否protected
ACC_STATIC0x0008方法是否static
ACC_FINAL0x0010方法是否final
ACC_SYNCHRONIZED0x0020方法是否synchronized
ACC_BRIDGE0x0040方法是否是由编译器产生的桥接方法
ACC_VARARGS0x0080方法是否接受不定参数
ACC_NATIVE0x0100方法是否native
ACC_ABSTRACT0x0400方法是否abstract
ACC_STRICT0x0800方法是否strictfp
ACC_SYNTHETIC0x1000方法是否是由编译器自动产生的

字节码为00 0A 00 0B,指向常量池第10和第11个常量,类型为CONSTACT_Utf8_info,值分别为<init>和()V。name_index代表方法名,descriptor_index代表方法的描述符。

说明该方法名为<init>,描述符为()V。

attribute_info则是该字段的属性表,那么属性表是什么样的结构呢。

2.7 属性表集合

ClassFile {
    u2             attributes_count;
    attribute_info attributes[attributes_count];
    .......
}

对应的字节码为:00 01 00 0C 00 00 00 1D

作用:Class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息

字节码详解:

其中前两个字节0x0001转换为十进制为1,说明有1个属性。

attribute_info常量类型的数据结构为

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

attribute_name_index为属性名。字节码为00 0C,指向常量池第12个常量,类型为CONSTACT_Utf8_info,值为Code,说明这是一个Code属性。

00 00 00 1D说明后面属性内容的长度,1D为十进制的29,说明后面29个字节的内容为属性内容。那么这个Code属性的属性作用和结构内容是怎么样的呢?

Code属性作用:方法中的代码经过编译器变为的字节码指令,存储在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];
}

字节码详解:00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0D

这里从max_stack开始,max_stack描述了操作数栈深度的最大值,这里是00 01说明最大值为1

max_locals描述了局部变量表所需的存储空间,单位是Slot,对于double和long这两种64位的需要两个Slot存放,其余的byte、char、float、int、short、boolean和returnAddress等长度不超过32位的则用1个Slot存放。

Slot可以被重用,当代码执行超过局部变量的作用域,该变量占用的Slot可以被其他Slot占用,所以max_locals不等于方法用到了多少个局部变量,而是编译器根据变量的作用域计算出max_locals的大小。

code_length和code存储Java源程序编译后生成的字节码指令,code_length为00 05,说明后面有5个字节为字节码指令,为2A B7 00 01 B1,根据指令表,2A对应指令为aload_0,含义是将第0个Slot中为reference类型的本地变量推送到操作数栈顶;B7对应指令为invokespecial,作用是以栈顶的reference类型的数据所指向的对象作为方法接收者,调用对象的实例构造方法,后跟两个字节是他的参数,说明调用哪一个方法;00 01是参数,指向常量池第1个常量,类型为CONSTACT_Methodref_info,值为java/lang/Object."<init>":()V;B1对应指令为return,含义是返回此方法,且返回值为void,执行该条指令后,方法结束。

exception_table用于实现Java一场及finally处理机制,00 00说明定义了0个异常。

最后的是attribute_info,说明Code属性的属性,00 01说明有1个属性,结构是一样的。

字节码为00 0D,指向常量池第13个常量,类型为CONSTACT_Utf8_info,值为LineNumberTable,说明这是一个LineNumberTable属性。

LineNumberTable属性作用:描述Java源码行号与字节码行号之间的对应关系
LineNumberTable属性表结构

LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number;	
    } line_number_table[line_number_table_length];
}

字节码详解:00 00 00 06 00 01 00 00 00 01

00 00 00 06说明后面6个字节为属性描述。

line_number_table_length说明line_number_table长度,00 01说明后续有1个line_number_table_length。

line_number_table包含两个数据项,其中start_pc是字节码行号,line_number是Java源码行号。

00 00 00 01说明行号数据是0:1。

还有很多属性,如下。

每个属性都有不同的结构及作用,读者可以自行修改源码,从而发现不同的属性。

属性名称使用位置含义
Code 方法表Java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量值
Deprecated类、方法表、字段表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
EnclosingMethod类文件仅当一个类为局部类或者匿名类时才能拥有这个属性,这个属性用于标识这个类所在的外围方法
InnerClasses类文件内部类列表
LineNumberTableCode属性Java源码的行号与字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
StackMapTableCode属性JDK1.6新增属性,供新的类型检查验证器检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配
Signature类、方法表、字段表用于支持泛型情况下的方法签名
SourceFile类文件记录源文件名称
SourceDebugExtension类文件存储额外的调试信息
Synthetic类、方法表、字段表标识方法或字段为编译器自动生成的
LocalVariableTypeTable使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
RuntimeVisibleAnnotations类、方法表、字段表为动态注解提供支持,指明哪些注解是运行时可见的
RuntimeInvisibleAnnotations类、方法表、字段表指明哪些注解是运行时不可见的
RuntimeVisibleParameterAnnotations方法表与RuntimeVisibleAnnotations类似,作用对象为方法参数
RuntimeInvisibleParameterAnnotations方法表与RuntimeInvisibleAnnotations类似,作用对象为方法参数
AnootationDefault方法表记录注解类元素的默认值
BootstrapMethods类文件保存invokedynamic指令引用的引导方法限定符

后面的字节码也无需在分析下去了,后面还有两个方法,一个属性,按照这个结构继续解析下去,就可以发现,跟javap反编译出来的描述是一致的。

Classfile /D:/用户目录/我的文档/Code/JVMSearch/Person.class
  Last modified 2020-12-19; size 513 bytes
  MD5 checksum dea507ac0ecc8c9063c778c6f748a2bb
  Compiled from "Person.java"
class Person
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#19         // java/lang/Object."<init>":()V
   #2 = Fieldref           #20.#21        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Fieldref           #6.#22         // Person.name:Ljava/lang/String;
   #4 = Methodref          #23.#24        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #25            // wenxl
   #6 = Class              #26            // Person
   #7 = Class              #27            // java/lang/Object
   #8 = Utf8               name
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               <clinit>
  #17 = Utf8               SourceFile
  #18 = Utf8               Person.java
  #19 = NameAndType        #10:#11        // "<init>":()V
  #20 = Class              #28            // java/lang/System
  #21 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #22 = NameAndType        #8:#9          // name:Ljava/lang/String;
  #23 = Class              #31            // java/io/PrintStream
  #24 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #25 = Utf8               wenxl
  #26 = Utf8               Person
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  Person();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: getstatic     #3                  // Field name:Ljava/lang/String;
         6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         9: return
      LineNumberTable:
        line 6: 0
        line 7: 9

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #5                  // String wenxl
         2: putstatic     #3                  // Field name:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 3: 0
}
SourceFile: "Person.java"

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值