java 0000 0001 0002 000a 000b_Java代码编译后的class文件
还是熟悉的味道,还是最简单的代码。
// 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文件相关推荐
- java 0000 0001 0002 000a 000b_同样的 Java 文件,为什么直接用 Javac 编译和使用 Maven 编译出来所得到的字节码不一样?...
如图,上面的是直接编译的结果,下面是用 Maven 编译的结果,为什么结果会不一样? 直接编译结果: cafe babe 0000 0033 000f 0a00 0300 0c07 000d 0700 ...
- java编译生成哪些文件_java编译后生成什么文件?生成的文件包括什么?
在大家编译完java程序的时候,都是会生成一个文件的,作为java新手不清楚java编译后生成什么文件?那么今天我们就给大家讲解一下这方面的内容,大家可以参考下文哦! java文件编译过后会生成一个c ...
- 深入理解:cmd下java命令启动JVM运行class文件时,可以自动识别不同编码编译后的class文件并加之运行
cmd下java命令启动JVM运行class文件时,可以自动识别不同编码编译后的class文件并加之运行 总结如下: 一:程序源代码以GBK格式保存时: 二:程序以源代码UTF-8无BOM格式保存时: ...
- 通过自定义Gradle插件修改编译后的class文件
我的简书同步发布:通过自定义Gradle插件修改编译后的class文件 转载请注明出处:[huachao1001的专栏:http://blog.csdn.net/huachao1001] 或许你会觉得 ...
- weblogic对JSP预编译、weblogic读取JSP编译后的class文件、ant中weblogic.jspc预编译JSP
我们都知道在weblogic中JSP是每次第一次访问的时候才会编译,这就造成第一次访问某个JSP的时候性能下降,有时候我们也希望JSP被编译成class然后打包在jar中实现隐藏JSP的功能,下面介绍 ...
- target存放的是编译后的.class文件地方 默认情况下不会讲非class文件放入进入 如果要使用非.class文件 需要通过增加配置方式自动加入文件...
target存放的是编译后的.class文件地方 默认情况下不会讲非class文件放入进入 如果要使用非.class文件 需要通过增加配置方式自动加入文件 转载于:https://www.cnblog ...
- Java 编译后的class文件覆盖注意要点
今天在tomcat中出现了一个错误,是一个常量文件写入错误.修改了常量Java类文件编译后覆盖并没有解决问题.最初以为是缓存严重,清除了缓存,kill了Java进程还是问题还是依旧.最后没有办法,用j ...
- cython代码编译和setup.py文件编写
Cython 官方文档: https://cython.readthedocs.io/en/latest/ 中文文档:https://www.bookstack.cn/read/cython-doc- ...
- Linux环境下开发板Tiny4412应用,实现交叉编译及minicom的调配,将代码编译后下载到开发板并运行
一.实验目的 1.熟悉Linux环境,学习使用命令行操控计算机系统,学会基础的ubuntu机器操作. 2.初步学习使用开发板Tiny4412,查看实验说明以及开发板说明书,学习基本使用步骤. 3.安装 ...
最新文章
- ubuntu vscode_如何在Ubuntu-18.04下用VSCode编译LibTorch
- (转载)H.264码流的RTP封包说明
- 聊一聊bypass information_schema
- Windows 用户和内核模式
- bzoj5369 [Pkusc2018]最大前缀和
- 七.OpenCv图像轮廓
- Canvas 画贝塞尔曲线(二阶曲线和三阶曲线)
- 《触动人心设计优秀iphone应用》读后感
- 昨夜今晨全球大公司动态
- 网络连接正常但百度网页打不开显示无法访问此网站解决方案
- 如何确保数据完整性?
- Linux环境下运行matlab以及执行m文件
- Mysql技术-innodb引擎-笔记
- 中国5级省市编码 在线查询服务
- 使用cmake构建工程
- Mac下常用快捷键和chrome插件
- 麦肯锡工作法-读书心得
- java毕业设计大学生学科竞赛管理系统mybatis+源码+调试部署+系统+数据库+lw
- 第一个入驻阿里云自营心选商城,如今它已经是营收过亿的SaaS独角兽
- 作为程序员的他,大学四年一直自学,全靠这些实用工具和学习网站