java javap
什么是Javap,如何使用它以及何时要反汇编类文件?
作为Java开发工具包(JDK)的一部分,我们可以使用许多工具,这些工具有助于更好地理解Java代码。 这些工具之一是javap命令,它使我们能够在后台传递Java类文件。
在下面的文章中,我们将研究javap,了解它如何帮助我们并确切地了解我们如何使用它。 代码,反汇编!
什么是javap?
javap是一个命令行工具,可分解Java类文件:它分解了我们的类文件,并揭示了其中的内容。 该工具将二进制格式的类文件转换为人类可读的代码。 好吧,对于某些人。
javap提供了许多输出,这些输出根据我们感兴趣的数据而有所不同。默认情况下,javap打印每个类的非私有成员的声明。
至于javap中的p代表什么,所有证据都指向“打印”,因为javap命令会打印出类中的字节码。
我们可以使用javap的一种好方法是探索异常。 如果您想提高自己的知识并了解引发异常时会发生什么,请查看我们的帖子,了解有关Java异常的令人惊讶的真相 。
在我们的类中使用javap
既然我们知道了javap是什么,该是在我们的代码上尝试使用它的时候了。 我们通过键入命令,选择一个选项并添加类名称来做到这一点:
javap [选项]类名
正如我们指出的,这些选项(也可以称为标志)将决定我们的输出是什么。 我们可以从许多标志中进行选择,包括:
- -l –输出行和局部变量表
- -public –仅显示公共班级和成员
- -protected –仅显示受保护的和公开的班级和成员
- -package –仅显示软件包,受保护的和公共的类和成员
- -p –显示所有班级和成员
- -Jflag –将标志直接传递到运行时系统
- -s –打印内部类型签名
- -sysinfo –显示正在处理的类的系统信息(路径,大小,日期,MD5哈希)
- -constants –显示静态最终常量
- -c –打印反汇编的代码
- -verbose –打印堆栈大小,方法的局部变量和args
用javap深入字节码
在列出了可以使用javap进行的操作之后,现在该弄清楚它的实际工作方式了。 为此,我们创建了一个名为ExampleClass的基本类:
public class ExampleClass
{private int a = 0;private int b = 0;public static void main(String[] args) {System.out.println("Hello world!");}
}现在让我们在javap的帮助下更深入地研究它。 首先,我们将使用不带任何其他标志的javap命令来打印出非私有成员。 我们的输出是这样的:
$ javap ExampleClassCompiled from "ExampleClass.java"
public class ExampleClass {public ExampleClass();public static void main(java.lang.String[]);
}如您所见,这是原始代码的漂亮“普通”视图,没有有关私有整数和逻辑的任何信息。 这是一个好的开始,但是如果我们想更深入地了解该怎么办? 让我们尝试使用-c打印出反汇编的代码:
$ javap -c ExampleClassCompiled from "ExampleClass.java"
public class ExampleClass {public ExampleClass();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: aload_05: iconst_06: putfield      #2                  // Field a:I9: aload_010: iconst_011: putfield      #3                  // Field b:I14: returnpublic static void main(java.lang.String[]);Code:0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #5                  // String Hello world!5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: return
}现在,我们以某种有点可读的形式保存了字节码,可以在其中识别我们的方法,整数,命令和字符串。 但是,等等,还有更多。 如果我们需要有关该课程的更多信息怎么办? 那是我们冗长的标志可以帮助我们的地方:
$ javap -v ExampleClassClassfile /Users/es/ExampleClass.classLast modified May 22, 2017; size 496 bytesMD5 checksum 7d29362228a3128e67b0c20c8bb54ee1Compiled from "ExampleClass.java"
public class ExampleClassSourceFile: "ExampleClass.java"minor version: 0major version: 51flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #8.#20         //  java/lang/Object."<init>":()V#2 = Fieldref           #7.#21         //  ExampleClass.a:I#3 = Fieldref           #7.#22         //  ExampleClass.b:I#4 = Fieldref           #23.#24        //  java/lang/System.out:Ljava/io/PrintStream;#5 = String             #25            //  Hello world!#6 = Methodref          #26.#27        //  java/io/PrintStream.println:(Ljava/lang/String;)V#7 = Class              #28            //  ExampleClass#8 = Class              #29            //  java/lang/Object#9 = Utf8               a#10 = Utf8               I#11 = Utf8               b#12 = Utf8               <init>#13 = Utf8               ()V#14 = Utf8               Code#15 = Utf8               LineNumberTable#16 = Utf8               main#17 = Utf8               ([Ljava/lang/String;)V#18 = Utf8               SourceFile#19 = Utf8               ExampleClass.java#20 = NameAndType        #12:#13        //  "<init>":()V#21 = NameAndType        #9:#10         //  a:I#22 = NameAndType        #11:#10        //  b:I#23 = Class              #30            //  java/lang/System#24 = NameAndType        #31:#32        //  out:Ljava/io/PrintStream;#25 = Utf8               Hello world!#26 = Class              #33            //  java/io/PrintStream#27 = NameAndType        #34:#35        //  println:(Ljava/lang/String;)V#28 = Utf8               ExampleClass#29 = Utf8               java/lang/Object#30 = Utf8               java/lang/System#31 = Utf8               out#32 = Utf8               Ljava/io/PrintStream;#33 = Utf8               java/io/PrintStream#34 = Utf8               println#35 = Utf8               (Ljava/lang/String;)V
{public ExampleClass();flags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: aload_05: iconst_06: putfield      #2                  // Field a:I9: aload_010: iconst_011: putfield      #3                  // Field b:I14: returnLineNumberTable:line 1: 0line 3: 4line 4: 9public static void main(java.lang.String[]);flags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #5                  // String Hello world!5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 7: 0line 8: 8
}很多行在我们面前展开。 我们可以看到以下内容:
 1.元数据–此类文件的位置,上次修改时间以及具有其唯一ID的类哈希 
 2.常量池–结构表,它引用ClassFile结构及其子结构中的各种常量。 它保存类,接口,类实例,字段名称,字符串常量或数组的符号信息。 
 3.字节码–用JVM可以“读取”的语言编写的指令集 
在OverOps,我们使用javap作为研究工具来挖掘类内部。 我们最近使用它来发现IBM J9的工作方式,为使用此JVM的开发人员提供支持。 如果您想了解更多有关我们如何帮助团队减少调试时间并知道代码在何时何地中断的原因, 请单击此处以安排演示 。
现在我们知道了如何使用javap,是时候回答一个重要的问题了:
到底有什么好处呢?
Javap很酷,特别是如果您是像我们这样的数据迷并且想知道代码背后发生了什么时。 但是除了不错的代码探索冒险外,它也非常有用。
对于原始源代码不可用的情况,它可能会派上用场,其中显示了可以使用的方法。 它还可以帮助找出别人的代码或3rd party类中的内容。
我们还可以将javap用作Java类的类验证器,以确保每个加载的类文件都以适当的方式进行结构化。
刚开始,javap就像是一根魔杖,可以在代码内为我们提供所有信息,但它并不是对我们所面临的每个问题的完整解决方案。 它可能会帮助“逆向工程”某些代码,但这更多是一个复杂难题的线索。
如果您正在寻找一种将javap字节码输出转换为功能性Java代码的方法,而不仅仅是“数据列表”,那么您将需要反编译工具(如JD , Mocha等)的帮助。
最后的想法
尽管您不会经常使用它,但是javap是一个有用的工具,请牢记。 除了它是一个很酷的“技巧”之外,它还更深入地介绍了Java代码,向我们展示了幕后发生的事情以及JVM如何使用我们的代码。
翻译自: https://www.javacodegeeks.com/2017/06/javap-usage-unfolds-whats-hidden-inside-java-class-files.html
java javap