javap学习总结
javap的基本用法
http://blog.csdn.net/hantiannan/article/details/7659904javap是JDK自帶的反匯編器,可以查看java編譯器為我們生成的字節碼。通過它,我們可以對照源代碼和字節碼,從而了解很多編譯器內部的工作。
語法:
javap [ 命令選項 ] class. . .
javap 命令用于解析類文件。其輸出取決于所用的選項。若沒有使用選項,javap 將輸出傳遞給它的類的 public 域及方法。javap 將其輸出到標準輸出設備上。
命令選項
-help 輸出 javap 的幫助信息。
-l 輸出行及局部變量表。
-b 確保與 JDK 1.1 javap 的向后兼容性。
-public 只顯示 public 類及成員。
-protected 只顯示 protected 和 public 類及成員。
-package 只顯示包、protected 和 public 類及成員。這是缺省設置。
-private 顯示所有類和成員。
-J[flag] 直接將 flag 傳給運行時系統。
-s 輸出內部類型簽名。
-c 輸出類中各方法的未解析的代碼,即構成 Java 字節碼的指令。
-verbose 輸出堆棧大小、各方法的 locals 及 args 數,以及class文件的編譯版本
-classpath[路徑] 指定 javap 用來查找類的路徑。如果設置了該選項,則它將覆蓋缺省值或 CLASSPATH 環境變量。目錄用冒號分隔。
? -bootclasspath[路徑] 指定加載自舉類所用的路徑。缺省情況下,自舉類是實現核心 Java 平臺的類,位于 jrelib下面。
-extdirs[dirs] 覆蓋搜索安裝方式擴展的位置。擴展的缺省位置是 jrelibext。
英文說明:
C:\>javap -help
Usage: javap <options> <classes>...
where options include:
? ?-c ? ? ? ? ? ? ? ? ? ? ? ?Disassemble the code
? ?-classpath <pathlist> ? ? Specify where to find user class files
? ?-extdirs <dirs> ? ? ? ? ? Override location of installed extensions
? ?-help ? ? ? ? ? ? ? ? ? ? Print this usage message
? ?-J<flag> ? ? ? ? ? ? ? ? ?Pass <flag> directly to the runtime system
? ?-l ? ? ? ? ? ? ? ? ? ? ? ?Print line number and local variable tables
? ?-public ? ? ? ? ? ? ? ? ? Show only public classes and members
? ?-protected ? ? ? ? ? ? ? ?Show protected/public classes and members
? ?-package ? ? ? ? ? ? ? ? ?Show package/protected/public classes
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?and members (default)
? ?-private ? ? ? ? ? ? ? ? ?Show all classes and members
? ?-s ? ? ? ? ? ? ? ? ? ? ? ?Print internal type signatures
? ?-bootclasspath <pathlist> Override location of class files loaded
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?by the bootstrap class loader
? ?-verbose ? ? ? ? ? ? ? ? ?Print stack size, number of locals and args for methods
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?If verifying, print reasons for failure
示例:
下面也經典的StringBuilder代替String做字符串的例子。
[java] view plain copy print?
public class JAVAPTest { ?
? ? public static void main(String[] args) { ?
??
? ? } ?
??
? ? public static String contactWithStringNoLoopNoPara() { ?
? ? ? ? String s = "This is " + " my " + "first JAVAP test code."; ?
? ? ? ? return s; ?
? ? } ?
? ? ??
? ? public static String contactWithStringNoLoop(int count) { ?
? ? ? ? String s = "This is " + " my " + count + "th JAVAP test code."; ?
? ? ? ? return s; ?
? ? } ?
? ? ??
? ? public static String contactWithStringLoop(int count) { ?
? ? ? ? String s = ""; ?
? ? ? ? for (int i = 0; i < count; i++) { ?
? ? ? ? ? ? s += i; ?
? ? ? ? } ?
? ? ? ? return s; ?
? ? } ?
??
? ? public static String contactWithStringBufferLoop(int count) { ?
? ? ? ? StringBuffer sb = new StringBuffer(); ?
? ? ? ? for (int i = 0; i < count; i++) { ?
? ? ? ? ? ? sb.append(i); ?
? ? ? ? } ?
? ? ? ? return sb.toString(); ?
? ? } ?
} ?
先編譯:javac JAVAPTest.java
執行反編譯:javap -c JAVAPTest ? ? ? ? //注意這個地方不需要class后綴。
結果如下:
[java] view plain copy print?
Compiled from "JAVAPTest.java" ?
public class JAVAPTest extends java.lang.Object{ ?
public JAVAPTest(); ?
? Code: ?
? ?0: ? aload_0 ?
? ?1: ? invokespecial ? #1; //Method java/lang/Object."<init>":()V ?
? ?4: ? return ?
??
public static void main(java.lang.String[]); ?
? Code: ?
? ?0: ? return ?
??
public static java.lang.String contactWithStringNoLoopNoPara(); ?
? Code: ?
? ?0: ? ldc ? ? #2; //String This is ?my first JAVAP test code. ?
? ?2: ? astore_0 ?
? ?3: ? aload_0 ?
? ?4: ? areturn ?
??
public static java.lang.String contactWithStringNoLoop(int); ?
? Code: ?
? ?0: ? new ? ? #3; //class java/lang/StringBuilder ?
? ?3: ? dup ?
? ?4: ? invokespecial ? #4; //Method java/lang/StringBuilder."<init>":()V ?
? ?7: ? ldc ? ? #5; //String This is ?my ?
? ?9: ? invokevirtual ? #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; ?
? ?12: ?iload_0 ?
? ?13: ?invokevirtual ? #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; ?
? ?16: ?ldc ? ? #8; //String th JAVAP test code. ?
? ?18: ?invokevirtual ? #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; ?
? ?21: ?invokevirtual ? #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; ?
? ?24: ?astore_1 ?
? ?25: ?aload_1 ?
? ?26: ?areturn ?
??
public static java.lang.String contactWithStringLoop(int); ?
? Code: ?
? ?0: ? ldc ? ? #10; //String ?
? ?2: ? astore_1 ?
? ?3: ? iconst_0 ?
? ?4: ? istore_2 ?
? ?5: ? iload_2 ?
? ?6: ? iload_0 ?
? ?7: ? if_icmpge ? ? ? 35 ?
? ?10: ?new ? ? #3; //class java/lang/StringBuilder ?
? ?13: ?dup ?
? ?14: ?invokespecial ? #4; //Method java/lang/StringBuilder."<init>":()V ?
? ?17: ?aload_1 ?
? ?18: ?invokevirtual ? #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; ?
? ?21: ?iload_2 ?
? ?22: ?invokevirtual ? #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; ?
? ?25: ?invokevirtual ? #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; ?
? ?28: ?astore_1 ?
? ?29: ?iinc ? ?2, 1 ?
? ?32: ?goto ? ?5 ?
? ?35: ?aload_1 ?
? ?36: ?areturn ?
??
public static java.lang.String contactWithStringBufferLoop(int); ?
? Code: ?
? ?0: ? new ? ? #11; //class java/lang/StringBuffer ?
? ?3: ? dup ?
? ?4: ? invokespecial ? #12; //Method java/lang/StringBuffer."<init>":()V ?
? ?7: ? astore_1 ?
? ?8: ? iconst_0 ?
? ?9: ? istore_2 ?
? ?10: ?iload_2 ?
? ?11: ?iload_0 ?
? ?12: ?if_icmpge ? ? ? 27 ?
? ?15: ?aload_1 ?
? ?16: ?iload_2 ?
? ?17: ?invokevirtual ? #13; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer; ?
? ?20: ?pop ?
? ?21: ?iinc ? ?2, 1 ?
? ?24: ?goto ? ?10 ?
? ?27: ?aload_1 ?
? ?28: ?invokevirtual ? #14; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; ?
? ?31: ?areturn ?
??
} ?
有這個結果我們可以知道。
1。contactWithStringNoLoopNoPara方法中,代碼里面是字符串拼接,貌似需要是用StringBuilder替換的好。其實在看了上面的反編譯結果后,已經自動組合成一個固定字符串了。因此完全沒有必要使用StringBuilder。
[java] view plain copy print?
0: ? ldc ? ? #2; //String This is ?my first JAVAP test code. ?
2。contactWithStringNoLoop方法中,因為使用到了變量,貌似需要是用StringBuilder替換的好。其實在看了上面的反編譯結果后,已經自動使用了StringBuilder。所以代碼也沒有必要使用StringBuilder。
3. contactWithStringLoop方法中,是循環拼接字符串,貌似需要是用StringBuilder替換的好。看了反編譯后,每個循環里面都各自生成了一個StringBuilder,并將StringBuilder.toString()防賦值給我們的Sring變量。而我們希望的是只生成一個StringBuilder對象。因此改為StringBuilder的好。循環的時候改為contactWithBufferLoop的方法最好。
4.contactWithBufferLoop方法中,是循環拼接字符串。也是我們預想的步驟在執行。
========
使用javap反編譯Java字節碼文件
http://www.365mini.com/page/javap-disassemble-class-file-code.htm在上一篇文章《StringBuilder、StringBuffer與Java字符串處理》中,我們反匯編了Java字節碼文件,通過查看編譯器編譯后的字節命令,我們能夠更清楚地了解Java在字符串拼接等方面的處理機制。
那么,我們如何反編譯指定的Java字節碼文件呢?其實,在Sun公司提供的JDK中,就已經內置了Java字節碼文件反編譯工具javap.exe(位于JDK安裝目錄的bin文件夾下)。
我們可以在dos窗口中使用javap來反匯編指定的Java字節碼文件。在使用javap的相關dos命令之前,你必須確保已經將JDK安裝目錄\bin添加到環境變量path中。
接著,我們就可以使用javap來反編譯指定的Java字節碼文件了。在此之前,我們先通過javap的幫助命令javap -help查看javap相關指令的用法。
javap-help
從上述內容我們可以知道,javap的使用命令格式為javap 選項參數 類名,其中選項參數可以有多個,中間用空格隔開,也可以一個都沒有。下面我們編寫如下源代碼文件(包名test,類名Person),并將其編譯為Person.class字節碼文件。
package test;public class Person {
? ? public Person(String name, int age, boolean gender, String address) {
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? ? ? this.gender = gender;
? ? ? ? this.address = address;
? ? }
? ? private String name; // private修飾符
? ? int age; // 默認無訪問修飾符(即下面所說的package、friendly)
? ? protected boolean gender; // protected修飾符
? ? public String address; // public修飾符
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
? ? public void sayHi() {
? ? ? ? System.out.println("Hello, my name is " + this.name);
? ? }
}
接著將包名文件夾test及Person.class放置在D:\java目錄下。以便于我們使用javap命令進行測試。
java-person-class
在執行命令之前,我們需要將dos窗口的當前工作目錄變更為D:\java\test。
cd-current-dir
1、使用不帶任何選項參數的命令:javap Person
javap-person
javap Person和javap -package Person的顯示結果一樣,因為-package選項參數是默認的,用于顯示package(不帶任何訪問修飾符,即我們常說的friendly)、protected、public修飾的類或成員。
備注:在dos下進入工作目錄D:\java,然后使用命令javap test.Person也可以實現上述操作。下同。
2、使用命令:javap -public Person顯示public修飾的類或成員。
javap-public-person
與此類似,選項參數-protected用于顯示protected以上訪問級別(protected、public)的類或成員;選項參數-private用于顯示private以上訪問級別,也就是所有的類或成員。
3、使用命令:javap -public -l Person顯示public修飾的類或成員,并顯示行號表格和本地變量表格。
javap-public-l-person
4、使用命令:javap -c Person顯示Person.class反匯編出的字節碼命令。
javap-c-person
由于選項參數之間組合較多,因此其他選項參數不再一一截圖贅述,僅在下面使用文字進行說明:
-classpath <pathlist>
手動指定用戶class字節碼文件的存放目錄,javap程序將在此目錄下查找class文件,多個路徑以英文分號分隔。例如:javap -classpath D:\java\test Person(即使DOS窗口的當前工作目錄為其他任意路徑,該命令均可正確執行)。
-s
打印變量的內部類型簽名,例如:javap -classpath D:\java\test -s Person。
-extdirs <dirs>
指定javap搜索已安裝的java擴展的位置,默認的java擴展的位置為jre\lib\ext。例如:javap -classpath D:\java\test -extdirs D:\java\myext Person
-bootclasspath <pathlist>
指定使用Java底層類加載器(bootstrap class loader)加載的字節碼文件的位置。例如:javap -classpath D:\java\test -bootclasspath D:\java\core Person
-verbose
打印方法參數和本地變量的數量以及棧區大小。
-J<flag>
使用javap.exe來執行java.exe虛擬機的相關命令,例如javap -J-version相當于java -version,可以有多個命令,中間以空格隔開。
========
在eclipse中使用javap
http://blog.sina.com.cn/s/blog_6e5e78bf0101okuj.htmljavap是sun提供的對class文件進行反編譯的工具
1、配置Run---external tools---external tools configurations
選擇Program 新建javap運行方式
設置location、workspace等選項
如下圖:
需要注意的是workspace選擇和argument配置
workding directory 設置為${workspace_loc}/${project_name} ,
Arguments: ?
? ? ? ? ?-c -verbose -classpath ${workspace_loc}/${project_name}/bin ${java_type_name}
如果設置錯誤會提示cannot find xxx類的錯誤,或者是有關java_type_name empty的錯誤
arguments要加上java_type_name,否則會提示No classes were specified on the command line
2、Run
Compiled from "HelloWorld.java"
public class testJava.src.HelloWorld extends java.lang.Object
? SourceFile: "HelloWorld.java"
? minor version: 0
? major version: 50
? Constant pool:
const #1 = class #2; // ?testJava/src/HelloWorld
const #2 = Asciz testJava/src/HelloWorld;
const #3 = class #4; // ?java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz ;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Method #3.#9; // ?java/lang/Object."":()V
const #9 = NameAndType #5:#6;// ?"":()V
const #10 = Asciz LineNumberTable;
const #11 = Asciz LocalVariableTable;
const #12 = Asciz this;
const #13 = Asciz LtestJava/src/HelloWorld;;
const #14 = Asciz main;
const #15 = Asciz ([Ljava/lang/String;)V;
const #16 = Field #17.#19; // ?java/lang/System.out:Ljava/io/PrintStream;
const #17 = class #18; // ?java/lang/System
const #18 = Asciz java/lang/System;
const #19 = NameAndType #20:#21;// ?out:Ljava/io/PrintStream;
const #20 = Asciz out;
const #21 = Asciz Ljava/io/PrintStream;;
const #22 = String #23; // ?Hello, world!
const #23 = Asciz Hello, world!;
const #24 = Method #25.#27; // ?java/io/PrintStream.println:(Ljava/lang/String;)V
const #25 = class #26; // ?java/io/PrintStream
const #26 = Asciz java/io/PrintStream;
const #27 = NameAndType #28:#29;// ?println:(Ljava/lang/String;)V
const #28 = Asciz println;
const #29 = Asciz (Ljava/lang/String;)V;
const #30 = Asciz args;
const #31 = Asciz [Ljava/lang/String;;
const #32 = Asciz SourceFile;
const #33 = Asciz HelloWorld.java;
{
public testJava.src.HelloWorld();
? Code:
? ?Stack=1, Locals=1, Args_size=1
? ?0: aload_0
? ?1: invokespecial #8; //Method java/lang/Object."":()V
? ?4: return
? LineNumberTable:?
? ?line 3: 0
? LocalVariableTable:?
? ?Start ?Length ?Slot ?Name ? Signature
? ?0 ? ? ?5 ? ? ?0 ? ?this ? ? ? LtestJava/src/HelloWorld;
public static void main(java.lang.String[]);
? Code:
? ?Stack=2, Locals=1, Args_size=1
? ?0: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
? ?3: ldc #22; //String Hello, world!
? ?5: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
? ?8: return
? LineNumberTable:?
? ?line 10: 0
? ?line 12: 8
? LocalVariableTable:?
? ?Start ?Length ?Slot ?Name ? Signature
? ?0 ? ? ?9 ? ? ?0 ? ?args ? ? ? [Ljava/lang/String;
}
另外點擊javap運行按鈕時 先選中需要被運行的.java文件
========
javap(反匯編命令)詳解
http://blog.csdn.net/hudashi/article/details/7062668?javap是JDK自帶的反匯編器,可以查看java編譯器為我們生成的字節碼。通過它,我們可以對照源代碼和字節碼,從而了解很多編譯器內部的工作。
語法:
javap [ 命令選項 ] class. . .
javap 命令用于解析類文件。其輸出取決于所用的選項。若沒有使用選項,javap 將輸出傳遞給它的類的 public 域及方法。javap 將其輸出到標準輸出設備上。
命令選項
-help 輸出 javap 的幫助信息。
-l 輸出行及局部變量表。
-b 確保與 JDK 1.1 javap 的向后兼容性。
-public 只顯示 public 類及成員。
-protected 只顯示 protected 和 public 類及成員。
-package 只顯示包、protected 和 public 類及成員。這是缺省設置。
-private 顯示所有類和成員。
-J[flag] 直接將 flag 傳給運行時系統。
-s 輸出內部類型簽名。
-c 輸出類中各方法的未解析的代碼,即構成 Java 字節碼的指令。
-verbose 輸出堆棧大小、各方法的 locals 及 args 數,以及class文件的編譯版本
-classpath[路徑] 指定 javap 用來查找類的路徑。如果設置了該選項,則它將覆蓋缺省值或 CLASSPATH 環境變量。目錄用冒號分隔。
? - bootclasspath[路徑] 指定加載自舉類所用的路徑。缺省情況下,自舉類是實現核心 Java 平臺的類,位于 jrelib
t.jar 和 jrelibi18n.jar 中。
-extdirs[dirs] 覆蓋搜索安裝方式擴展的位置。擴展的缺省位置是 jrelibext。
實例1:
Hello.java文件
public class Hello
{
static void main(String args[])
{
int i=10;
int j=100;
int m=i+j;
System.out.println("m:"+m);
}
int get()
{
int a=1;
int b=2;
int c=3;
int d=4;
int e=5;
int f=6;
int n=a+b+c+d+e+f;
return n;
}
static int get2()
{
int a=1;
int b=12;
int c=39;
int d=a+b;
return d;
}
}
然后再執行以下命令:
javac Hello.java
javap -c Hello
得到
Compiled from "Hello.java"
public class Hello extends java.lang.Object{
public Hello();
? Code:
? ?0:
aload_0
? ?1:
invokespecial #1; //Method java/lang/Object."<init>":()V
? ?4:
return
static void main(java.lang.String[]);
? Code:
? ?0:
bipush
10
? ?2:
istore_1
? ?3:
bipush
100
? ?5:
istore_2
? ?6:
iload_1
? ?7:
iload_2
? ?8:
iadd
? ?9:
istore_3
? ?10:
getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
? ?13:
new #3; //class java/lang/StringBuilder
? ?16:
dup
? ?17:
invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
? ?20:
ldc #5; //String m:
? ?22:
invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
? ?25:
iload_3
? ?26:
invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
? ?29:
invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
? ?32:
invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
? ?35:
return
int get();
? Code:
? ?0:
iconst_1
? ?1:
istore_1
? ?2:
iconst_2
? ?3:
istore_2
? ?4:
iconst_3
? ?5:
istore_3
? ?6:
iconst_4
? ?7:
istore 4
? ?9:
iconst_5
? ?10:
istore 5
? ?12:
bipush 6
? ?14:
istore
6
? ?16:
iload_1
? ?17:
iload_2
? ?18:
iadd
? ?19:
iload_3
? ?20:
iadd
? ?21:
iload 4
? ?23:
iadd
? ?24:
iload 5
? ?26:
iadd
? ?27:
iload 6
? ?29:
iadd
? ?30:
istore 7
? ?32:
iload 7
? ?34:
ireturn
static int get2();
? Code:
? ?0:
iconst_1
? ?1:
istore_0
? ?2:
bipush 12
? ?4:
istore_1
? ?5:
bipush 39
? ?7:
istore_2
? ?8:
iload_0
? ?9:
iload_1
? ?10:
iadd
? ?11:
istore_3
? ?12:
iload_3
? ?13:
ireturn
}
另外關于如何使用javap命令查看class文件的編譯版本請參考《用javap查看編譯版本》
========
總結
- 上一篇: 图解windbg查看Win7结构体
- 下一篇: 图解MyEclipse用DB Brows