还是熟悉的味道,还是最简单的代码。

// Hello.java

public class Hello {

public static void main(String[] args) {

System.out.println("Hello World!");

}

}

使用javac编译 .java 代码,得到同名的 .class文件,然后使用 java 命令,执行类名就可以运行了。

$ javac Hello.java

$ java Hello

Hello World!

编译后的class文件,以十六进制格式显示,长这个样子:

cafe babe 0000 0034 001d 0a00 0600 0f09

0010 0011 0800 120a 0013 0014 0700 1507

0016 0100 063c 696e 6974 3e01 0003 2829

5601 0004 436f 6465 0100 0f4c 696e 654e

756d 6265 7254 6162 6c65 0100 046d 6169

6e01 0016 285b 4c6a 6176 612f 6c61 6e67

2f53 7472 696e 673b 2956 0100 0a53 6f75

7263 6546 696c 6501 000a 4865 6c6c 6f2e

6a61 7661 0c00 0700 0807 0017 0c00 1800

1901 000c 4865 6c6c 6f20 576f 726c 6421

0700 1a0c 001b 001c 0100 0548 656c 6c6f

0100 106a 6176 612f 6c61 6e67 2f4f 626a

6563 7401 0010 6a61 7661 2f6c 616e 672f

5379 7374 656d 0100 036f 7574 0100 154c

6a61 7661 2f69 6f2f 5072 696e 7453 7472

6561 6d3b 0100 136a 6176 612f 696f 2f50

7269 6e74 5374 7265 616d 0100 0770 7269

6e74 6c6e 0100 1528 4c6a 6176 612f 6c61

6e67 2f53 7472 696e 673b 2956 0021 0005

0006 0000 0000 0002 0001 0007 0008 0001

0009 0000 001d 0001 0001 0000 0005 2ab7

0001 b100 0000 0100 0a00 0000 0600 0100

0000 0100 0900 0b00 0c00 0100 0900 0000

2500 0200 0100 0000 09b2 0002 1203 b600

04b1 0000 0001 000a 0000 000a 0002 0000

0003 0008 0004 0001 000d 0000 0002 000e

这就是可供Java虚拟机执行的字节码文件,也是Java之所以能够实现,一次编译多次运行的根本原因。

把代码编译成一个中间状态的字节码,而不是直接运行在操作系统上的机器码,使得跨系统的工作就完全交给Java虚拟机了。这样一来,就可以大大减少开发人员的适配工作量,从而提高开发效率,这就是所谓的平台无关性。

采用Java虚拟机的架构设计,为语言扩展留下了空间,相当于给代码编译和操作系统做了一个中间件,只要将代码编译成字节码文件,就都可以运行在Java虚拟机上,这也是为什么会有Scala 、Kotlin、Groovy等多种语言都可以与Java混合编码的原因,这就是所谓的语言无关性。

既然我们看到了这个字节码文件,那就得好好解读一下了。其实只要是代码编译的文件,都是有约定的规范格式的,字节码也是一样的。

1. class文件结构

类型

名称

说明

备注或示例对照

u4

magic

魔数,识别class文件

cafe babe

u2

minor_version

副版本号

0000

u2

major_version

主版本号

0034

u2

constant_pool_count

常量池计数器

001d,十进制值为29

cp_info

constant_pool

常量池

常量个数为constant_pool_count-1,

示例即为28个常量

u2

access_flags

访问标志

0021

u2

this_class

类索引

0005

u2

super_class

父类索引

0006

u2

interfaces_count

接口计数器

0000

u2

interfaces

接口索引集合

u2

fields_count

字段个数

0000

field_info

fields

字段集合

u2

methods_count

方法计数器

0002

method_info

methods

方法集合

u2

attributes_count

附加属性计数器

0001

attribute_info

attributes

附加属性集合

000d 0000 0002 000e

class文件伪结构中只有两种数据类型:无符号数和表。

无符号数属于基本的数据类型,以u1、u2、u4和u8来分表代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。

表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯地以"_info"结尾。表用于描述有层次关系的复合结构的数据,而整个class文件本质上就是一张表。

2. 常量类型

类型

标志

描述

CONSTANT_Utf8_info

1

UTF-8编码的字符串

CONSTANT_Integer_info

3

整形字面量

CONSTANT_Float_info

4

浮点型字面量

CONSTANT_Long_info

5

长整型字面量

CONSTANT_Double_info

6

双精度浮点型字面量

CONSTANT_Class_info

7

类或接口的符号引用

CONSTANT_String_info

8

字符串类型字面量

CONSTANT_Fieldref_info

9

字段的符号引用

CONSTANT_Methodref_info

10

类中方法的符号引用

CONSTANT_InterfaceMethodref_info

11

接口中方法的符号引用

CONSTANT_NameAndType_info

12

字段或方法的符号引用

CONSTANT_MethodHandle_info

15

表示方法句柄

CONSTANT_MothodType_info

16

标识方法类型

CONSTANT_InvokeDynamic_info

18

表示一个动态方法调用点

3. 常量结构

常量

项目

类型

描述

CONSTANT_Utf8_info

tag

u1

值为1

length

u2

utf-8编码的字符串占用的字节数

bytes

u1

长度为length的utf-8编码的字符串

CONSTANT_Integer_info

tag

u1

值为3

bytes

u4

按照高位在前存储的int值

CONSTANT_Float_info

tag

u1

值为4

bytes

u4

按照高位在前存储的floatt值

CONSTANT_Long_info

tag

u1

值为5

bytes

u8

按照高位在前存储的long值

CONSTANT_Double_info

tag

u1

值为6

bytes

u8

按照高位在前存储double值

CONSTANT_Class_info

tag

u1

值为7

index

u2

指向全限定名常量的索引

CONSTANT_String_info

tag

u1

值为8

index

u2

指向字符串字面量的索引

CONSTANT_Fieldref_info

tag

u1

值为9

index

u2

指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项

index

u2

指向字段描述符CONSTANT_NameAndType_info的索引项

CONSTANT_Methodref_info

tag

u1

值为10

index

u2

指向声明方法的类描述符CONSTANT_Class_info的索引项

index

u2

指向名称及类型描述符CONSTANT_NameAndType_info的索引项

CONSTANT_InterfaceMethodref_info

tag

u1

值为11

index

u2

指向声明方法的接口描述符CONSTANT_Class_info的索引项

index

u2

指向名称及类型描述符CONSTANT_NameAndType_info的索引项

CONSTANT_NameAndType_info

tag

u1

值为12

index

u2

指向该字段或方法名称常量项的索引

index

u2

指向该字段或方法描述符常量项的索引

CONSTANT_MethodHandle_info

tag

u1

值为15

reference_kind

u1

值必须是1-9,它决定了方法句柄的类型,方法句柄类型的值表示方法句柄的字节码行为

reference_index

u2

值必须是对常量池的有效索引

CONSTANT_MothodType_info

tag

u1

值为16

descriptor_index

u2

值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符

CONSTANT_InvokeDynamic_info

tag

u1

值为18

bootstrap_method_attr_index

u2

值必须是对当前Class文件中引导方法表示的bootstrap_methods[]数组的有效索引

name_and_type_index

u2

值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符

人工解读示例字节码中的常量池部分

0a 0013 0014 0700 1507

0016 0100 063c 696e 6974 3e01 0003 2829

5601 0004 436f 6465 0100 0f4c 696e 654e

756d 6265 7254 6162 6c65 0100 046d 6169

6e01 0016 285b 4c6a 6176 612f 6c61 6e67

2f53 7472 696e 673b 2956 0100 0a53 6f75

7263 6546 696c 6501 000a 4865 6c6c 6f2e

6a61 7661 0c00 0700 0807 0017 0c00 1800

1901 000c 4865 6c6c 6f20 576f 726c 6421

0700 1a0c 001b 001c 0100 0548 656c 6c6f

0100 106a 6176 612f 6c61 6e67 2f4f 626a

6563 7401 0010 6a61 7661 2f6c 616e 672f

5379 7374 656d 0100 036f 7574 0100 154c

6a61 7661 2f69 6f2f 5072 696e 7453 7472

6561 6d3b 0100 136a 6176 612f 696f 2f50

7269 6e74 5374 7265 616d 0100 0770 7269

6e74 6c6e 0100 1528 4c6a 6176 612f 6c61

6e67 2f53 7472 696e 673b 2956

index

十六进制码

长度

类型或备注

1

0a

1

CONSTANT_Methodref_info

0006

2

#6

000f

2

#15

2

09

1

CONSTANT_Fieldref_info

0010

2

#10

0011

2

#11

3

08

1

CONSTANT_String_info

0012

2

#18

4

0a

1

CONSTANT_Methodref_info

0013

2

#19

0014

2

#20

5

07

1

CONSTANT_Class_info

0015

2

#21

6

07

1

CONSTANT_Class_info

0016

2

#22

7

01

1

CONSTANT_Utf8_info

0006

2

length=6

3c 696e 6974 3e

6

8

01

1

CONSTANT_Utf8_info

0003

2

length=3

2829 56

3

()V

9

01

1

CONSTANT_Utf8_info

0004

2

length=4

436f 6465

4

Code

10

01

1

CONSTANT_Utf8_info

000f

2

length=15

4c 696e 654e 756d 6265 7254 6162 6c65

15

LineNumberTable

11

01

1

CONSTANT_Utf8_info

0004

2

6d 6169 6e

4

main

12

01

1

CONSTANT_Utf8_info

0016

2

285b 4c6a 6176 612f 6c61 6e672f53 7472 696e 673b 2956

22

([Ljava/lang/String;)V

13

01

1

CONSTANT_Utf8_info

000a

2

53 6f757263 6546 696c 65

10

SourceFile

14

01

1

CONSTANT_Utf8_info

000a

2

4865 6c6c 6f2e 6a61 7661

10

Hello.java

15

0c

1

CONSTANT_NameAndType_info

0007

2

0008

2

16

07

1

CONSTANT_Class_info

0017

2

17

0c

1

CONSTANT_NameAndType_info

0018

2

0019

2

18

01

1

CONSTANT_Utf8_info

000c

2

4865 6c6c 6f20 576f 726c 6421

12

Hello World!

19

07

1

CONSTANT_Class_info

001a

2

20

0c

1

CONSTANT_NameAndType_info

001b

2

001c

2

21

01

1

CONSTANT_Utf8_info

0005

2

48 656c 6c6f

5

Hello

22

01

1

CONSTANT_Utf8_info

0010

2

6a 6176 612f 6c61 6e67 2f4f 626a 6563 74

16

java/lang/Object

23

01

1

CONSTANT_Utf8_info

0010

2

6a61 7661 2f6c 616e 672f 5379 7374 656d

16

java/lang/System

24

01

1

CONSTANT_Utf8_info

0003

2

6f 7574

3

out

25

01

1

CONSTANT_Utf8_info

0015

2

4c 6a61 7661 2f69 6f2f 5072 696e 7453 74726561 6d3b

21

Ljava/io/PrintStream;

26

01

1

CONSTANT_Utf8_info

0013

2

6a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d

19

java/io/PrintStream

27

01

1

CONSTANT_Utf8_info

0007

2

70 7269 6e74 6c6e

7

println

28

01

1

CONSTANT_Utf8_info

0015

2

28 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956

21

(Ljava/lang/String;)V

4. 类访问标志

标志名称

标志值

含义

ACC_PUBLIC

0x0001

是否为public类型

ACC_FINAL

0x0010

是否被声明为final,只有类可以设置

ACC_SUPER

0x0020

是否允许使用invokespecial字节码指令的新语义,JDK1.0.2之后编译出来的类的这个标志默认为真

ACC_INTERFACE

0x0200

标志这是一个接口

ACC_ABSTRACT

0x0400

是否为abstract类型,对于接口或者抽象类来说,此标志值为真,其他类型为假

ACC_SYNTHETIC

0x1000

标志这个类并非由用户代码产生

ACC_ANNOTATION

0x2000

标志这是一个注解

ACC_ENUM

0x4000

标志这是一个枚举

5. 方法表结构

类型

名称

数量

u2

access_flags

1

u2

name_index

1

u2

descriptor_index

1

u2

attributes_count

1

attribute_info

attributes

attributes_count

6. 方法访问标志

标志名称

标志值

含义

ACC_PUBLIC

0x0001

方法是否为public

ACC_PRIVATE

0x0002

方法是否为private

ACC_PROTECTED

0x0004

方法是否为protected

ACC_STATIC

0x0008

方法是否为static

ACC_FINAL

0x0010

方法是否为final

ACC_SYHCHRONRIZED

0x0020

方法是否为synchronized

ACC_BRIDGE

0x0040

方法是否是有编译器产生的方法

ACC_VARARGS

0x0080

方法是否接受参数

ACC_NATIVE

0x0100

方法是否为native

ACC_ABSTRACT

0x0400

方法是否为abstract

ACC_STRICTFP

0x0800

方法是否为strictfp

ACC_SYNTHETIC

0x1000

方法是否是有编译器自动产生的

7. 通用属性结构表

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u1

info

attribute_length

人工解读示例字节码中的方法部分

0002 0001 0007 0008 0001

0009 0000 001d 0001 0001 0000 0005 2ab7

0001 b100 0000 0100 0a00 0000 0600 0100

0000 0100 0900 0b00 0c00 0100 0900 0000

2500 0200 0100 0000 09b2 0002 1203 b600

04b1 0000 0001 000a 0000 000a 0002 0000

0003 0008 0004

十六进制码

类型或备注

0002

方法个数为2

第一个方法

0001

方法标志为public

0007

方法名索引为7,

0008

方法描述符索引为8,()V

0001

attribute个数为1

第一个属性

0009

属性索引为9, Code

0000 001d

属性值长度为29

0001 0001 0000 0005 2ab7 0001 b100 0000 0100 0a00 0000 0600 0100 0000 01

29个字节

第二个方法

00 09

方法标志为public,static

00 0b

方法名索引为11,main

00 0c

方法描述符索引为12,([Ljava/lang/String;)V

00 01

attribute个数为1

第一个属性

00 09

属性索引为9, Code

00 0000 25

属性值长度为37

00 0200 0100 0000 09b2 0002 1203 b600 04b1 0000 0001 000a 0000 000a 0002 0000 0003 0008 0004

37个字节

属性类型比较多,字段、接口和方法中都存在,但是都可以按照通用结构进行字节拆分。各个属性有自己的表结构,不做逐一列举,只列出常用的Code结构做栗子。

8. Code属性结构表

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

max_stack

1

u2

max_locals

1

u4

code_length

1

u1

code

code_length

u2

exception_table_length

1

exception_info

exception_table

exception_table_length

u2

attributes_count

1

attribute_info

attributes

attributes_count

逐字节解读虽然繁琐,但是能加深对字节码的结构的理解。不过,在掌握了结构原理之后,使用工具才是最实在的,毕竟人工解读的效率太低了。

$ javap -verbose Hello.class

Classfile /Users/cage/Study/java_cmd/Hello.class

Last modified 2020-3-15; size 416 bytes

MD5 checksum e2e1c1e330c7df4592041d815080126b

Compiled from "Hello.java"

public class Hello

minor version: 0

major version: 52

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref #6.#15 // java/lang/Object."":()V

#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;

#3 = String #18 // Hello World!

#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V

#5 = Class #21 // Hello

#6 = Class #22 // java/lang/Object

#7 = Utf8

#8 = Utf8 ()V

#9 = Utf8 Code

#10 = Utf8 LineNumberTable

#11 = Utf8 main

#12 = Utf8 ([Ljava/lang/String;)V

#13 = Utf8 SourceFile

#14 = Utf8 Hello.java

#15 = NameAndType #7:#8 // "":()V

#16 = Class #23 // java/lang/System

#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;

#18 = Utf8 Hello World!

#19 = Class #26 // java/io/PrintStream

#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V

#21 = Utf8 Hello

#22 = Utf8 java/lang/Object

#23 = Utf8 java/lang/System

#24 = Utf8 out

#25 = Utf8 Ljava/io/PrintStream;

#26 = Utf8 java/io/PrintStream

#27 = Utf8 println

#28 = Utf8 (Ljava/lang/String;)V

{

public Hello();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()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: ldc #3 // String Hello World!

5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

8: return

LineNumberTable:

line 3: 0

line 4: 8

}

SourceFile: "Hello.java"

有兴趣的同学,平时可以经常看看字节码文件及其结构,看都用到了哪些字节码指令,这样对理解Java代码的执行会更有好处。

java 0000 0001 0002 000a 000b_Java代码编译后的class文件相关推荐

  1. java 0000 0001 0002 000a 000b_同样的 Java 文件,为什么直接用 Javac 编译和使用 Maven 编译出来所得到的字节码不一样?...

    如图,上面的是直接编译的结果,下面是用 Maven 编译的结果,为什么结果会不一样? 直接编译结果: cafe babe 0000 0033 000f 0a00 0300 0c07 000d 0700 ...

  2. java编译生成哪些文件_java编译后生成什么文件?生成的文件包括什么?

    在大家编译完java程序的时候,都是会生成一个文件的,作为java新手不清楚java编译后生成什么文件?那么今天我们就给大家讲解一下这方面的内容,大家可以参考下文哦! java文件编译过后会生成一个c ...

  3. 深入理解:cmd下java命令启动JVM运行class文件时,可以自动识别不同编码编译后的class文件并加之运行

    cmd下java命令启动JVM运行class文件时,可以自动识别不同编码编译后的class文件并加之运行 总结如下: 一:程序源代码以GBK格式保存时: 二:程序以源代码UTF-8无BOM格式保存时: ...

  4. 通过自定义Gradle插件修改编译后的class文件

    我的简书同步发布:通过自定义Gradle插件修改编译后的class文件 转载请注明出处:[huachao1001的专栏:http://blog.csdn.net/huachao1001] 或许你会觉得 ...

  5. weblogic对JSP预编译、weblogic读取JSP编译后的class文件、ant中weblogic.jspc预编译JSP

    我们都知道在weblogic中JSP是每次第一次访问的时候才会编译,这就造成第一次访问某个JSP的时候性能下降,有时候我们也希望JSP被编译成class然后打包在jar中实现隐藏JSP的功能,下面介绍 ...

  6. target存放的是编译后的.class文件地方 默认情况下不会讲非class文件放入进入 如果要使用非.class文件 需要通过增加配置方式自动加入文件...

    target存放的是编译后的.class文件地方 默认情况下不会讲非class文件放入进入 如果要使用非.class文件 需要通过增加配置方式自动加入文件 转载于:https://www.cnblog ...

  7. Java 编译后的class文件覆盖注意要点

    今天在tomcat中出现了一个错误,是一个常量文件写入错误.修改了常量Java类文件编译后覆盖并没有解决问题.最初以为是缓存严重,清除了缓存,kill了Java进程还是问题还是依旧.最后没有办法,用j ...

  8. cython代码编译和setup.py文件编写

    Cython 官方文档: https://cython.readthedocs.io/en/latest/ 中文文档:https://www.bookstack.cn/read/cython-doc- ...

  9. Linux环境下开发板Tiny4412应用,实现交叉编译及minicom的调配,将代码编译后下载到开发板并运行

    一.实验目的 1.熟悉Linux环境,学习使用命令行操控计算机系统,学会基础的ubuntu机器操作. 2.初步学习使用开发板Tiny4412,查看实验说明以及开发板说明书,学习基本使用步骤. 3.安装 ...

最新文章

  1. ubuntu vscode_如何在Ubuntu-18.04下用VSCode编译LibTorch
  2. (转载)H.264码流的RTP封包说明
  3. 聊一聊bypass information_schema
  4. Windows 用户和内核模式
  5. bzoj5369 [Pkusc2018]最大前缀和
  6. 七.OpenCv图像轮廓
  7. Canvas 画贝塞尔曲线(二阶曲线和三阶曲线)
  8. 《触动人心设计优秀iphone应用》读后感
  9. 昨夜今晨全球大公司动态
  10. 网络连接正常但百度网页打不开显示无法访问此网站解决方案
  11. 如何确保数据完整性?
  12. Linux环境下运行matlab以及执行m文件
  13. Mysql技术-innodb引擎-笔记
  14. 中国5级省市编码 在线查询服务
  15. 使用cmake构建工程
  16. Mac下常用快捷键和chrome插件
  17. 麦肯锡工作法-读书心得
  18. java毕业设计大学生学科竞赛管理系统mybatis+源码+调试部署+系统+数据库+lw
  19. 第一个入驻阿里云自营心选商城,如今它已经是营收过亿的SaaS独角兽
  20. 作为程序员的他,大学四年一直自学,全靠这些实用工具和学习网站

热门文章

  1. Typora操作指南
  2. Hadoop Streaming二次排序
  3. Python编程基础:第五十四节 排序Sort
  4. Best practice for JVM Tuning[转]
  5. Docker源码分析(四):Docker Daemon之NewDaemon实现
  6. 利用solr的 DataImportHandler从mysql数据库建立索引
  7. Istio:一个用于微服务间通信的服务网格开源项目
  8. lvs为何不能完全替代DNS轮询--转
  9. php中的魔术函数以及魔术常量
  10. 1X1 convolution layers