編寫(xiě)HelloWorld.java
public class HelloWorld{
public static void main(String[] args){
System.out.println("HelloWorld");
}
}
編譯:
javac HelloWorld.java
javap的使用:
D:\>javap
用法: javap <options> <classes>
其中, 可能的選項(xiàng)包括:
-help --help -? 輸出此用法消息
-version 版本信息
-v -verbose 輸出附加信息
-l 輸出行號(hào)和本地變量表
-public 僅顯示公共類(lèi)和成員
-protected 顯示受保護(hù)的/公共類(lèi)和成員
-package 顯示程序包/受保護(hù)的/公共類(lèi)
和成員 (默認(rèn))
-p -private 顯示所有類(lèi)和成員
-c 對(duì)代碼進(jìn)行反匯編
-s 輸出內(nèi)部類(lèi)型簽名
-sysinfo 顯示正在處理的類(lèi)的
系統(tǒng)信息 (路徑, 大小, 日期, MD5 散列)
-constants 顯示最終常量
-classpath <path> 指定查找用戶(hù)類(lèi)文件的位置
-cp <path> 指定查找用戶(hù)類(lèi)文件的位置
-bootclasspath <path> 覆蓋引導(dǎo)類(lèi)文件的位置
其中-c -v -l -p -s
是最常用的
-c 選項(xiàng)
:
$ javap -c HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld(); // ##這里默認(rèn)會(huì)添加一個(gè)構(gòu)造方法
Code:
0: aload_0 //
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);//## main函數(shù)
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String HelloWorld
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
解釋?zhuān)?/p>
aload_0 這個(gè)操作碼是 aload_x 格式操作碼中的一個(gè)注暗。它們用來(lái)把**對(duì)象引用**加載到操作數(shù)棧。 x 表示正在被訪(fǎng)問(wèn)的局部變量數(shù)組的位置纲缓。在這里的 0 代表什么呢郁轻?我們知道非靜態(tài)的函數(shù)都有第一個(gè)默認(rèn)參數(shù)搁料,那就是 this熔脂,這里的 aload_0 就是把 this 入棧
-
invokespecial #1融虽,invokespecial 指令調(diào)用實(shí)例初始化方法眉睹、私有方法荔茬、父類(lèi)方法,#1 指的是常量池中的第一個(gè)竹海,這里是方法引用
java/lang/Object."<init>":()V慕蔚,也即構(gòu)造器函數(shù)
return,這個(gè)操作碼屬于 ireturn斋配、lreturn孔飒、freturn、dreturn艰争、areturn 和 return 操作碼組中的一員坏瞄,其中 i 表示 int,返回整數(shù)园细,同類(lèi)的還有 l 表示 long惦积,f 表示 float,d 表示 double猛频,a 表示 對(duì)象引用狮崩。沒(méi)有前綴類(lèi)型字母的 return 表示返回 void
-
getstatic #2,getstatic 獲取指定類(lèi)的靜態(tài)域鹿寻,并將其值壓入棧頂睦柴,#2 代表常量池中的第 2 個(gè),這里表示的是
java/lang/System.out:Ljava/io/PrintStream;毡熏,其實(shí)就是java.lang.System 類(lèi)的靜態(tài)變量 out(類(lèi)型是 PrintStream)
ldc #3坦敌、,ldc 用來(lái)將常量從運(yùn)行時(shí)常量池壓棧到操作數(shù)棧痢法,#3 代表常量池的第三個(gè)(字符串 Hello, World)
invokevirtual #4狱窘,invokevirutal 指令調(diào)用一個(gè)對(duì)象的實(shí)例方法,#4 表示 PrintStream.println(String) 函數(shù)引用财搁,并把棧頂兩個(gè)元素出棧
-v選項(xiàng)
$ javap -v HelloWorld.class
Classfile /D:/ideaproject/jvm/HelloWorld.class
Last modified 2019-6-19; size 411 bytes
MD5 checksum 30c120f376b16361b854fd5c0d695ea6
Compiled from "HelloWorld.java"
public class HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // HelloWorld
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #18 // HelloWorld
#6 = Class #21 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 HelloWorld.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #22 // java/lang/System
#17 = NameAndType #23:#24 // out:Ljava/io/PrintStream;
#18 = Utf8 HelloWorld
#19 = Class #25 // java/io/PrintStream
#20 = NameAndType #26:#27 // println:(Ljava/lang/String;)V
#21 = Utf8 java/lang/Object
#22 = Utf8 java/lang/System
#23 = Utf8 out
#24 = Utf8 Ljava/io/PrintStream;
#25 = Utf8 java/io/PrintStream
#26 = Utf8 println
#27 = Utf8 (Ljava/lang/String;)V
{
public HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
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: ldc #3 // String HelloWorld
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}
SourceFile: "HelloWorld.java"
-l選項(xiàng)
$ javap -l HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
LineNumberTable:
line 3: 0
line 4: 8
}
-s選項(xiàng):
輸出簽名的類(lèi)型描述符
$ javap -s HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();//構(gòu)造方法
descriptor: ()V //描述: ()表示沒(méi)有參數(shù)蘸炸,V表示返回值為void
public static void main(java.lang.String[]); //main方法
descriptor: ([Ljava/lang/String;)V // ([Ljava/lang/String;)表示參數(shù)為數(shù)組
}
修改java代碼如下:
public class HelloWorld{
public int test01(byte b1, short s, int a, long l2, float f1, double d, char c, boolean bool,Integer it,String str){
return 0;
}
}
重新編譯,查看方法簽名
$ javap -s HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
descriptor: ()V
public int test01(byte, short, int, long, float, double, char, boolean, java.lang.Integer, java.lang.String);
descriptor: (BSIJFDCZLjava/lang/Integer;Ljava/lang/String;)I
}
查看調(diào)試信息:
編譯階段加上-g參數(shù):
$ javac -g HelloWorld.java
-l 選項(xiàng)
$ javap -l HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LHelloWorld;
//this表明構(gòu)造方法默認(rèn)第一個(gè)參數(shù)是this尖奔,LHelloWorld搭儒,表示構(gòu)造方法名字和類(lèi)名一樣
public static void main(java.lang.String[]);
LineNumberTable:
line 4: 0
line 5: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
//args:參數(shù)名字穷当,[Ljava/lang/String:表示參數(shù)為String數(shù)組,數(shù)組以 '[L' 開(kāi)頭
public int test01(byte, short, int, long, float, double, char, boolean, java.lang.Integer, java.lang.String);
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LHelloWorld;//this表明成員方法默認(rèn)第一個(gè)參數(shù)是this
0 2 1 b1 B //第二個(gè)參數(shù)名字是b1淹禾, 類(lèi)型為byte馁菜,B代表byte
0 2 2 s S //第三個(gè)參數(shù)名字是s, 類(lèi)型為short铃岔,S代表short
0 2 3 a I //第四個(gè)參數(shù)名字是a汪疮, 類(lèi)型為int,I代表int
0 2 4 l2 J //第五個(gè)參數(shù)名字是l2毁习, 類(lèi)型為long铲咨,J代表long
0 2 6 f1 F //第六個(gè)參數(shù)名字是f1, 類(lèi)型為float蜓洪,F(xiàn)代表float
0 2 7 d D //第七個(gè)參數(shù)名字是d纤勒, 類(lèi)型為short,D代表double
0 2 9 c C //第八個(gè)參數(shù)名字是c隆檀, 類(lèi)型為char摇天,C代表char
0 2 10 bool Z //第九個(gè)參數(shù)名字是bool, 類(lèi)型為boolean恐仑,Z代表boolean
0 2 11 it Ljava/lang/Integer; //第十個(gè)參數(shù)名字是it泉坐, 類(lèi)型為Integer
//引用類(lèi)型以L(fǎng)開(kāi)頭
0 2 12 str Ljava/lang/String; //第十一個(gè)參數(shù)名字是str, 類(lèi)型為String
}
-v 選項(xiàng)
$ javap -v HelloWorld.class
Classfile /D:/ideaproject/jvm/HelloWorld.class
Last modified 2019-6-19; size 874 bytes
MD5 checksum ee15f31a14c1582634907a7504bf4cf7
Compiled from "HelloWorld.java"
public class HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#42 // java/lang/Object."<init>":()V
#2 = Fieldref #43.#44 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #45 // helloworld
#4 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #48 // HelloWorld
#6 = Class #49 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 LHelloWorld;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 test01
#19 = Utf8 (BSIJFDCZLjava/lang/Integer;Ljava/lang/String;)I
#20 = Utf8 b1
#21 = Utf8 B
#22 = Utf8 s
#23 = Utf8 S
#24 = Utf8 a
#25 = Utf8 I
#26 = Utf8 l2
#27 = Utf8 J
#28 = Utf8 f1
#29 = Utf8 F
#30 = Utf8 d
#31 = Utf8 D
#32 = Utf8 c
#33 = Utf8 C
#34 = Utf8 bool
#35 = Utf8 Z
#36 = Utf8 it
#37 = Utf8 Ljava/lang/Integer;
#38 = Utf8 str
#39 = Utf8 Ljava/lang/String;
#40 = Utf8 SourceFile
#41 = Utf8 HelloWorld.java
#42 = NameAndType #7:#8 // "<init>":()V
#43 = Class #50 // java/lang/System
#44 = NameAndType #51:#52 // out:Ljava/io/PrintStream;
#45 = Utf8 helloworld
#46 = Class #53 // java/io/PrintStream
#47 = NameAndType #54:#55 // println:(Ljava/lang/String;)V
#48 = Utf8 HelloWorld
#49 = Utf8 java/lang/Object
#50 = Utf8 java/lang/System
#51 = Utf8 out
#52 = Utf8 Ljava/io/PrintStream;
#53 = Utf8 java/io/PrintStream
#54 = Utf8 println
#55 = Utf8 (Ljava/lang/String;)V
{
public HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
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
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LHelloWorld;
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 helloworld
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 4: 0
line 5: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
public int test01(byte, short, int, long, float, double, char, boolean, java.lang.Integer, java.lang.String);
descriptor: (BSIJFDCZLjava/lang/Integer;Ljava/lang/String;)I
flags: ACC_PUBLIC
Code:
stack=1, locals=13, args_size=11 //參數(shù)個(gè)數(shù)為11
0: iconst_0
1: ireturn
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LHelloWorld;
0 2 1 b1 B
0 2 2 s S
0 2 3 a I
0 2 4 l2 J
0 2 6 f1 F
0 2 7 d D
0 2 9 c C
0 2 10 bool Z
0 2 11 it Ljava/lang/Integer;
0 2 12 str Ljava/lang/String;
}
SourceFile: "HelloWorld.java"
以16進(jìn)制查看HelloWorld.class文件:
# 將class文件轉(zhuǎn)換成16進(jìn)制
$ xxd HelloWorld.class HelloWorld.txt
$ ls
HelloWorld.class HelloWorld.java HelloWorld.txt
$ cat HelloWorld.txt
00000000: cafe babe 0000 0034 001c 0a00 0600 0f09 .......4........
00000010: 0010 0011 0800 120a 0013 0014 0700 1207 ................
00000020: 0015 0100 063c 696e 6974 3e01 0003 2829 .....<init>...()
00000030: 5601 0004 436f 6465 0100 0f4c 696e 654e V...Code...LineN
00000040: 756d 6265 7254 6162 6c65 0100 046d 6169 umberTable...mai
00000050: 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 n...([Ljava/lang
00000060: 2f53 7472 696e 673b 2956 0100 0a53 6f75 /String;)V...Sou
00000070: 7263 6546 696c 6501 000f 4865 6c6c 6f57 rceFile...HelloW
00000080: 6f72 6c64 2e6a 6176 610c 0007 0008 0700 orld.java.......
00000090: 160c 0017 0018 0100 0a48 656c 6c6f 576f .........HelloWo
000000a0: 726c 6407 0019 0c00 1a00 1b01 0010 6a61 rld...........ja
000000b0: 7661 2f6c 616e 672f 4f62 6a65 6374 0100 va/lang/Object..
000000c0: 106a 6176 612f 6c61 6e67 2f53 7973 7465 .java/lang/Syste
000000d0: 6d01 0003 6f75 7401 0015 4c6a 6176 612f m...out...Ljava/
000000e0: 696f 2f50 7269 6e74 5374 7265 616d 3b01 io/PrintStream;.
000000f0: 0013 6a61 7661 2f69 6f2f 5072 696e 7453 ..java/io/PrintS
00000100: 7472 6561 6d01 0007 7072 696e 746c 6e01 tream...println.
00000110: 0015 284c 6a61 7661 2f6c 616e 672f 5374 ..(Ljava/lang/St
00000120: 7269 6e67 3b29 5600 2100 0500 0600 0000 ring;)V.!.......
00000130: 0000 0200 0100 0700 0800 0100 0900 0000 ................
00000140: 1d00 0100 0100 0000 052a b700 01b1 0000 .........*......
00000150: 0001 000a 0000 0006 0001 0000 0001 0009 ................
00000160: 000b 000c 0001 0009 0000 0025 0002 0001 ...........%....
00000170: 0000 0009 b200 0212 03b6 0004 b100 0000 ................
00000180: 0100 0a00 0000 0a00 0200 0000 0300 0800 ................
00000190: 0400 0100 0d00 0000 0200 0e ...........
class文件各個(gè)部分的解釋
-
魔數(shù)
cafe babe
class 的魔數(shù)為 0xCAFEBABE, 這個(gè)魔數(shù)是 JVM 識(shí)別 .class 文件的標(biāo)志裳仆,虛擬機(jī)在加載類(lèi)文件之前會(huì)先檢查這四個(gè)字節(jié)腕让,如果不是 0xCAFEBABE 則拒絕加載該文件