什么是 javap,如何使用它以及何时要反汇编类文件?
作为 Java 开发工具包 (JDK) 的一部分,我们可以使用许多工具,它们有助于更好地理解 Java 代码。其中一个工具是 javap 命令,它为我们提供了 Java 类文件的后台通道。
在接下来的文章中,我们将了解 javap,了解它如何帮助我们,并确切地了解我们如何使用它。代码,反汇编!
javap 是一个反汇编 Java 类文件的命令行工具:它拆开我们的类文件,并揭示里面的内容。该工具将二进制格式的类文件转换为人类可读的代码。好吧,对于某些人来说。
javap 提供了许多输出,它们根据我们感兴趣的数据而有所不同。默认情况下,javap 打印每个类的非私有成员的声明。
至于 javap 中的 p 代表什么,所有证据都指向“打印”,因为 javap 命令打印出类中的字节码。
我们可以使用 javap 的一个好方法是探索异常。如果您想提高知识水平并了解抛出异常时会发生什么,请查看我们关于Java 异常的惊人真相 的帖子。
现在我们知道 javap 是什么,是时候在我们的代码中尝试使用它了。我们通过键入命令、选择一个选项并添加类名来做到这一点:
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 ExampleClass Compiled from "ExampleClass.java" public class ExampleClass { public ExampleClass(); public static void main(java.lang.String[]); }
如您所见,这是我们原始代码的一个非常“简单”的视图,没有任何关于我们的私有整数和逻辑的信息。这是一个好的开始,但如果我们想看得更深呢?让我们尝试使用 -c 来打印反汇编代码:
$ javap -c ExampleClass Compiled from "ExampleClass.java" public class ExampleClass { public ExampleClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field a:I 9: aload_0 10: iconst_0 11: putfield #3 // Field b:I 14: return public 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;)V 8: return }
现在我们的字节码以某种可读的形式出现,我们可以在其中识别我们的方法、整数、命令和字符串。但是等等,还有更多。如果我们想要关于这个类的更多信息怎么办?这就是我们的详细标志可以帮助我们的地方:
$ javap -v ExampleClass Classfile /Users/es/ExampleClass.class Last modified May 22, 2017; size 496 bytes MD5 checksum 7d29362228a3128e67b0c20c8bb54ee1 Compiled from "ExampleClass.java" public class ExampleClass SourceFile: "ExampleClass.java" minor version: 0 major version: 51 flags: 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_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field a:I 9: aload_0 10: iconst_0 11: putfield #3 // Field b:I 14: return LineNumberTable: line 1: 0 line 3: 4 line 4: 9 public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 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;)V 8: return LineNumberTable: line 7: 0 line 8: 8 }
一大堆线在我们面前解开。我们可以看到以下内容:
1. 元数据——此类文件所在的位置、上次修改时间以及具有唯一 ID 的类散列 2. 常量池——引用 ClassFile 结构及其子结构中各种常量的结构表。它保存类、接口、类实例、字段名、字符串常量或数组的符号信息。 3. 字节码——用 JVM 可以“读取”的语言编写的指令集
我们在 OverOps 使用 javap 作为研究工具,深入研究类。我们最近使用它来了解 IBM J9 的工作原理,为使用此 JVM 的开发人员提供支持。如果您想详细了解我们如何帮助团队减少调试时间,并了解代码在生产中出现中断的位置、时间和原因 - 单击此处安排演示。
现在我们知道如何使用 javap,是时候回答一个重要的问题了:
Javap 很酷,特别是如果您像我们一样是数据迷并且想知道代码背后发生了什么。但除了美妙的代码探索冒险之外,它也非常有用。
对于原始源代码不可用的情况,它可能会派上用场,显示哪些方法可供您使用。它还可以帮助找出其他人的代码或第 3 方类中的内容。
我们还可以使用 javap 作为 Java 类的类验证器,确保每个加载的类文件都以适当的方式构建。
起初,javap 感觉就像一根魔杖,可以为我们提供代码中的每一条信息,但它并不是我们面临的所有问题的完整解决方案。它可能有助于对某些代码进行“逆向工程”,但它更像是复杂谜题中的一条线索。
如果您正在寻找一种方法将 javap 字节码输出转换为功能性 Java 代码,而不仅仅是“数据列表”,您需要反编译工具的帮助,例如 JD、摩卡咖啡等。
虽然您不会经常使用它,但 javap 是一个需要记住的有用工具。除了这是一个很酷的“技巧”之外,它还提供了对 Java 代码的更深入研究,向我们展示了幕后发生的事情以及 JVM 如何使用我们的代码。
标签2: Java教程地址:https://www.cundage.com/article/jcg-javap-usage-unfolds-whats-hidden-inside-java-class-files.html