美文网首页Java 杂谈程序员javatool
JDK自带的反编译工具 javap

JDK自带的反编译工具 javap

作者: pjmike | 来源:发表于2018-09-06 17:52 被阅读0次

前言

以前经常看一些文章使用 javac 反编译 class文件,然后生成一堆字节码,再一顿骚操作分析字节码,可谓是非常炫酷。这里有时间刚好也来玩玩JDK的 javap

javap 介绍

javap是 JDK自带的一个工具,可以将 class文件反编译成字节码,它并没有将class文件反编译成 java文件,但是依然反编译成程序员能读的格式。

下面举一个小例子,java源代码如下:

public class JavapTest2 {
    private String username;

    public void say(String username) {
        System.out.println("hi,"+username);
    }
}

将其编译后,使用 javap来查询 JavapTest2的字节码

javac JavapTest2.java
javap -p -v JavapTest2

生成的字节码如下:

Classfile ../JavapTest2.class
  Last modified 2018-8-31; size 608 bytes
  MD5 checksum 25f04ad8674616cb2f0e7fe9d35e6ab1
  Compiled from "JavapTest2.java"
public class com.pjmike.JVM.JavapTest2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#21        // java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #24            // java/lang/StringBuilder
   #4 = Methodref          #3.#21         // java/lang/StringBuilder."<init>":()V
   #5 = String             #25            // hi,
   #6 = Methodref          #3.#26         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/String
Builder;
   #7 = Methodref          #3.#27         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = Methodref          #28.#29        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = Class              #30            // com/pjmike/JVM/JavapTest2
  #10 = Class              #31            // java/lang/Object
  #11 = Utf8               username
  #12 = Utf8               Ljava/lang/String;
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               say
  #18 = Utf8               (Ljava/lang/String;)V
  #19 = Utf8               SourceFile
  #20 = Utf8               JavapTest2.java
  #21 = NameAndType        #13:#14        // "<init>":()V
  #22 = Class              #32            // java/lang/System
  #23 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
  #24 = Utf8               java/lang/StringBuilder
  #25 = Utf8               hi,
  #26 = NameAndType        #35:#36        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #27 = NameAndType        #37:#38        // toString:()Ljava/lang/String;
  #28 = Class              #39            // java/io/PrintStream
  #29 = NameAndType        #40:#18        // println:(Ljava/lang/String;)V
  #30 = Utf8               com/pjmike/JVM/JavapTest2
  #31 = Utf8               java/lang/Object
  #32 = Utf8               java/lang/System
  #33 = Utf8               out
  #34 = Utf8               Ljava/io/PrintStream;
  #35 = Utf8               append
  #36 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #37 = Utf8               toString
  #38 = Utf8               ()Ljava/lang/String;
  #39 = Utf8               java/io/PrintStream
  #40 = Utf8               println
{
  private java.lang.String username;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE

  public com.pjmike.JVM.JavapTest2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0

  public void say(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=2
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #5                  // String hi,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/
lang/StringBuilder;
        15: aload_1
        16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/
lang/StringBuilder;
        19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 11: 0
        line 12: 25
}
SourceFile: "JavapTest2.java"

默认情况下 javap 会打印所有非私有的字段和方法,如下:

javap JavapTest2

Compiled from "JavapTest2.java"
public class com.pjmike.JVM.JavapTest2 {
  public com.pjmike.JVM.JavapTest2();
  public void say(java.lang.String);
}

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 -v -p JavapTest2 。加了 -p 选项后,还会打印私有的字段和方法,加上 -v 选项后,它会尽可能地打印出所有信息,如果只需要查询相关方法对应的字节码,可以使用 -c 代替 -v,代码如下:

Compiled from "JavapTest2.java"
public class com.pjmike.JVM.JavapTest2 {
  private java.lang.String username;

  public com.pjmike.JVM.JavapTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void say(java.lang.String);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: ldc           #5                  // String hi,
      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      25: return
}

可以看出少了很多附加信息,让我们更加专心的去关注方法对应的字节码。

下面简要分析下 say方法中的打印语句,里面涉及了字符串的拼接操作:

  1. 首先是new 指令,创建类实例的指令,在Java源代码的字符串拼接,到了编译器在编译阶段使用 StringBuilder类进行优化
    3: new           #3                  // class java/lang/StringBuilder
  1. 然后 invokespecial指令,用于调用实例初始化方法,将 StringBuilder对象初始化
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V

  1. ldc将"hi"字符串常量加载到操作数栈,然后invokevirtual指令用于调用对象的实例方法,这里调用 StringBuilderappend()拼接字符串的方法
      10: ldc           #5                  // String hi,
      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
  1. 最后调用 StringBuildertoString(),将拼接后的字符串输出
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

以上非常简要的分析了 字符串拼接的字节码操作,更多关于字节码的指令介绍,请参阅相关文档

小结

关于 javap 以及相关字节码知识目前还是接触不多,这里只是简单玩一玩javap,更多字节码相关的知识以及其他反编译工具,如`jad,cfr等还需要后续进一步深入探究。

相关文章

  • 构造代码块

    javap进行反编译 jdk提供了一个开发工具(jdk/bin/javap.exe)供我们进行反编译。 javap...

  • JDK自带的反编译工具 javap

    前言 以前经常看一些文章使用 javac 反编译 class文件,然后生成一堆字节码,再一顿骚操作分析字节码,可谓...

  • <>之javap

    一、引言 javap是JDK提供的一个命令行工具,javap能对给定的class文件提供的字节代码进行反编译。有人...

  • 8、线上问题排查

    文章主要列出了常见的排查工具,也有部分线上问题案例; JVM 问题排查 JDK工具包 javap 反编译工具jav...

  • JVM性能优化 - JDK内置命令工具

    1. JDK内置工具 - javap java反编译工具,主要用于根据Java字节码文件反汇编为Java源代码文件...

  • javap命令简述

    一、javap命令简述 javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对...

  • 210816:查看class编译版本-List集合去重-将一个表

    一. 查看class编译版本 1. javap命令 javap 命令行工具是用作反编译的,在反编译信息中,可以看到...

  • 通过javap命令分析java汇编指令

    王二北原创,转载请标明出处:来自王二北 一、javap命令简述 javap是jdk自带的反解析工具。它的作用就是根...

  • javap -c

    javap是sun提供的对class文件进行反编译的工具,javap就和javac、java等命令用法类似。 ja...

  • JDK内置工具命令

    javap Java反编译工具,主要用于根据Java字节码文件反汇编为Java源代码文件用法:javap

网友评论

    本文标题:JDK自带的反编译工具 javap

    本文链接:https://www.haomeiwen.com/subject/vrzxgftx.html