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 命令,執(zhí)行類名就可以運行了。
$ 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虛擬機執(zhí)行的字節(jié)碼文件,也是Java之所以能夠?qū)崿F(xiàn),一次編譯多次運行的根本原因。
把代碼編譯成一個中間狀態(tài)的字節(jié)碼,而不是直接運行在操作系統(tǒng)上的機器碼,使得跨系統(tǒng)的工作就完全交給Java虛擬機了。這樣一來,就可以大大減少開發(fā)人員的適配工作量,從而提高開發(fā)效率,這就是所謂的平臺無關性。
采用Java虛擬機的架構設計,為語言擴展留下了空間,相當于給代碼編譯和操作系統(tǒng)做了一個中間件,只要將代碼編譯成字節(jié)碼文件,就都可以運行在Java虛擬機上,這也是為什么會有Scala 、Kotlin、Groovy等多種語言都可以與Java混合編碼的原因,這就是所謂的語言無關性。
既然我們看到了這個字節(jié)碼文件,那就得好好解讀一下了。其實只要是代碼編譯的文件,都是有約定的規(guī)范格式的,字節(jié)碼也是一樣的。
1. class文件結構
類型
名稱
說明
備注或示例對照
u4
magic
魔數(shù),識別class文件
cafe babe
u2
minor_version
副版本號
0000
u2
major_version
主版本號
0034
u2
constant_pool_count
常量池計數(shù)器
001d,十進制值為29
cp_info
constant_pool
常量池
常量個數(shù)為constant_pool_count-1,
示例即為28個常量
u2
access_flags
訪問標志
0021
u2
this_class
類索引
0005
u2
super_class
父類索引
0006
u2
interfaces_count
接口計數(shù)器
0000
u2
interfaces
接口索引集合
u2
fields_count
字段個數(shù)
0000
field_info
fields
字段集合
u2
methods_count
方法計數(shù)器
0002
method_info
methods
方法集合
u2
attributes_count
附加屬性計數(shù)器
0001
attribute_info
attributes
附加屬性集合
000d 0000 0002 000e
class文件偽結構中只有兩種數(shù)據(jù)類型:無符號數(shù)和表。
無符號數(shù)屬于基本的數(shù)據(jù)類型,以u1、u2、u4和u8來分表代表1個字節(jié)、2個字節(jié)、4個字節(jié)和8個字節(jié)的無符號數(shù),無符號數(shù)可以用來描述數(shù)字、索引引用、數(shù)量值或者按照UTF-8編碼構成字符串值。
表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構成的復合數(shù)據(jù)類型,所有表都習慣地以"_info"結尾。表用于描述有層次關系的復合結構的數(shù)據(jù),而整個class文件本質(zhì)上就是一張表。
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
表示一個動態(tài)方法調(diào)用點
3. 常量結構
常量
項目
類型
描述
CONSTANT_Utf8_info
tag
u1
值為1
length
u2
utf-8編碼的字符串占用的字節(jié)數(shù)
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,它決定了方法句柄的類型,方法句柄類型的值表示方法句柄的字節(jié)碼行為
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[]數(shù)組的有效索引
name_and_type_index
u2
值必須是對當前常量池的有效索引,常量池在該索引處的項必須是CONSTANT_NameAndType_info結構,表示方法名和方法描述符
人工解讀示例字節(jié)碼中的常量池部分
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字節(jié)碼指令的新語義,JDK1.0.2之后編譯出來的類的這個標志默認為真
ACC_INTERFACE
0x0200
標志這是一個接口
ACC_ABSTRACT
0x0400
是否為abstract類型,對于接口或者抽象類來說,此標志值為真,其他類型為假
ACC_SYNTHETIC
0x1000
標志這個類并非由用戶代碼產(chǎn)生
ACC_ANNOTATION
0x2000
標志這是一個注解
ACC_ENUM
0x4000
標志這是一個枚舉
5. 方法表結構
類型
名稱
數(shù)量
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
方法是否是有編譯器產(chǎn)生的方法
ACC_VARARGS
0x0080
方法是否接受參數(shù)
ACC_NATIVE
0x0100
方法是否為native
ACC_ABSTRACT
0x0400
方法是否為abstract
ACC_STRICTFP
0x0800
方法是否為strictfp
ACC_SYNTHETIC
0x1000
方法是否是有編譯器自動產(chǎn)生的
7. 通用屬性結構表
類型
名稱
數(shù)量
u2
attribute_name_index
1
u4
attribute_length
1
u1
info
attribute_length
人工解讀示例字節(jié)碼中的方法部分
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
方法個數(shù)為2
第一個方法
0001
方法標志為public
0007
方法名索引為7,
0008
方法描述符索引為8,()V
0001
attribute個數(shù)為1
第一個屬性
0009
屬性索引為9, Code
0000 001d
屬性值長度為29
0001 0001 0000 0005 2ab7 0001 b100 0000 0100 0a00 0000 0600 0100 0000 01
29個字節(jié)
第二個方法
00 09
方法標志為public,static
00 0b
方法名索引為11,main
00 0c
方法描述符索引為12,([Ljava/lang/String;)V
00 01
attribute個數(shù)為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個字節(jié)
屬性類型比較多,字段、接口和方法中都存在,但是都可以按照通用結構進行字節(jié)拆分。各個屬性有自己的表結構,不做逐一列舉,只列出常用的Code結構做栗子。
8. Code屬性結構表
類型
名稱
數(shù)量
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
逐字節(jié)解讀雖然繁瑣,但是能加深對字節(jié)碼的結構的理解。不過,在掌握了結構原理之后,使用工具才是最實在的,畢竟人工解讀的效率太低了。
$ 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"
有興趣的同學,平時可以經(jīng)常看看字節(jié)碼文件及其結構,看都用到了哪些字節(jié)碼指令,這樣對理解Java代碼的執(zhí)行會更有好處。
總結
以上是生活随笔為你收集整理的java 0000 0001 0002 000a 000b_Java代码编译后的class文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 注解 数据字典_Spring实
- 下一篇: 命令行无法运行php,php不支持执行c