@使用javap反编译Java字节码文件

2023-02-12,,,,

在Sun公司提供的JDK中,就已经内置了Java字节文件反编译工具javap.exe(位于JDK安装目录的bin文件夹下)。

我们可以在dos窗口中使用javap来反汇编指定的Java字节码文件。在使用javap的相关dos命令之前,你必须确保已经将JDK安装目录\bin添加到环境变量path中。

接着,我们就可以使用javap来反编译指定的Java字节码文件了。在此之前,我们先通过javap的帮助命令javap -help查看javap相关指令的用法。

C:\Users\winner_0715>javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置

从上述内容我们可以知道,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修饰符
private String name;
// 默认无访问修饰符(即下面所说的package、friendly)
int age;
// protected修饰符
protected boolean gender;
// public修饰符
public String address; 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命令进行测试。

在执行命令之前,我们需要将dos窗口的当前工作目录变更为D:\java\test

1、使用不带任何选项参数的命令:javap Person

D:\java\test>javap Person
警告: 二进制文件Person包含test.Person
Compiled from "Person.java"
public class test.Person {
int age;
protected boolean gender;
public java.lang.String address;
public test.Person(java.lang.String, int, boolean, java.lang.String);
public java.lang.String getName();
public void setName(java.lang.String);
public void sayHi();
}

javap Person和javap -package Person的显示结果一样,因为-package选项参数是默认的,用于显示package(不带任何访问修饰符,即我们常说的friendly)、protected、public修饰的类或成员。不会显示私有成员的属性

备注:在dos下进入工作目录D:\java,然后使用命令javap test.Person也可以实现上述操作。下同。

2、使用命令:javap -public Person显示public修饰的类或成员。

D:\java\test>javap -public Person
警告: 二进制文件Person包含test.Person
Compiled from "Person.java"
public class test.Person {
public java.lang.String address;
public test.Person(java.lang.String, int, boolean, java.lang.String);
public java.lang.String getName();
public void setName(java.lang.String);
public void sayHi();
}

与此类似,选项参数-protected用于显示protected以上访问级别(protected、public)的类或成员;选项参数-private用于显示private以上访问级别,也就是所有的类或成员。

3、使用命令:javap -public -l Person显示public修饰的类或成员,并显示行号表格和本地变量表格。

D:\java\test>javap -public -l Person
警告: 二进制文件Person包含test.Person
Compiled from "Person.java"
public class test.Person {
public java.lang.String address; public test.Person(java.lang.String, int, boolean, java.lang.String);
LineNumberTable:
line 4: 0
line 5: 4
line 6: 9
line 7: 14
line 8: 19
line 9: 25
LocalVariableTable:
Start Length Slot Name Signature
0 26 0 this Ltest/Person;
0 26 1 name Ljava/lang/String;
0 26 2 age I
0 26 3 gender Z
0 26 4 address Ljava/lang/String; public java.lang.String getName();
LineNumberTable:
line 21: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Ltest/Person; public void setName(java.lang.String);
LineNumberTable:
line 25: 0
line 26: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Ltest/Person;
0 6 1 name Ljava/lang/String; public void sayHi();
LineNumberTable:
line 29: 0
line 30: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 this Ltest/Person;

4、使用命令:javap -c Person显示Person.class反汇编出的字节码命令。

D:\java\test>javap -c Person
警告: 二进制文件Person包含test.Person
Compiled from "Person.java"
public class test.Person {
int age; protected boolean gender; public java.lang.String address; public test.Person(java.lang.String, int, boolean, java.lang.String);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #2 // Field name:Ljava/lang/String;
9: aload_0
10: iload_2
11: putfield #3 // Field age:I
14: aload_0
15: iload_3
16: putfield #4 // Field gender:Z
19: aload_0
20: aload 4
22: putfield #5 // Field address:Ljava/lang/String;
25: return public java.lang.String getName();
Code:
0: aload_0
1: getfield #2 // Field name:Ljava/lang/String;
4: areturn public void setName(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field name:Ljava/lang/String;
5: return public void sayHi();
Code:
0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #7 // class java/lang/StringBuilder
6: dup
7: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
10: ldc #9 // String Hello, my name is
12: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_0
16: getfield #2 // Field name:Ljava/lang/String;
19: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
}

由于选项参数之间组合较多,因此其他选项参数不再一一截图赘述,仅在下面使用文字进行说明:

-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,可以有多个命令,中间以空格隔开。

@使用javap反编译Java字节码文件的相关教程结束。

《@使用javap反编译Java字节码文件.doc》

下载本文的Word格式文档,以方便收藏与打印。