javap 用法展开:Java 类文件中隐藏了什么?

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(308)   2023-12-10 07:14:57

什么是 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 – 打印堆栈大小、局部变量的数量和方法的参数

使用 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

相关阅读

Java HashSet 教程展示了如何使用 Java HashSet 集合。 Java哈希集 HashSet 是一个不包含重复元素的集合。此类为基本操作(添加、删除、包含和大小)提供恒定时间性...
SpringApplicationBuilder 教程展示了如何使用 SpringApplicationBuilder 创建一个简单的 Spring Boot 应用程序。 春天 是用于创建企业应...
通道是继 buffers 之后 java.nio 的第二个主要新增内容,我们在之前的教程中已经详细了解了这一点。通道提供与 I/O 服务的直接连接。 通道是一种在字节缓冲区和通道另一端的实体(通...
课程大纲 Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了一个分布式的、支持多租户的全文搜索引擎,带有 HTTP Web 界面和无模式的 JSON 文档。 Elasti...
解析器是强大的工具,使用 ANTLR 可以编写可用于多种不同语言的各种解析器。 在这个完整的教程中,我们将: 解释基础:什么是解析器,它可以用来做什么 查看如何设置 ANTLR 以便在 Java...
Java 是用于开发各种桌面应用程序、Web 应用程序和移动应用程序的最流行的编程语言之一。以下文章将帮助您快速熟悉 Java 语言,并迈向 API 和云开发等更复杂的概念。 1. Java语言...
Java中的继承是指子类继承或获取父类的所有非私有属性和行为的能力。继承是面向对象编程的四大支柱之一,用于提高层次结构中类之间的代码可重用性。 在本教程中,我们将了解 Java 支持的继承类型,...
Java Message Service 是一种支持正式通信的 API,称为 网络上计算机之间的消息传递。 JMS 为支持 Java 程序的标准消息协议和消息服务提供了一个通用接口。 JMS 提...
之前,我介绍了spring 3 + hibernate 集成 示例和struts 2 hello world 示例。在本教程中,我将讨论在将 spring 框架与 struts 与 hibern...
Java 项目中的一项常见任务是将日期格式化或解析为字符串,反之亦然。解析日期意味着你有一个代表日期的字符串,例如“2017-08-3”,你想把它转换成一个代表 Java 中日期的对象,例如Ja...