JDK поставляется с рядом полезных утилит, размещенных в каталоге инструментов bin. Для тех, кто хочет декомпилировать байт-код, особый интерес представляет Java утилита javap.

Утилита командной строки javap, также известный как дизассемблер файлов классов Java, выводит соответствующую информацию о любом скомпилированном классе Java, который его просят проверить. По умолчанию он просто выводит имена всех неприватных методов и переменных, объявленных в классе:

C:\_tools\java8\javap example\bin>javap com.mcnz.javap.example.CommandTutorial
Compiled from "CommandTutorial.java"
public class com.mcnz.javap.example.CommandTutorial extends java.util.ArrayList<java.lang.Object> {
protected static java.lang.String foo;
public java.lang.String bar;
public com.mcnz.javap.example.CommandTutorial();
void doStuff();
public static void main(java.lang.String[]);
public void useInnerType();
}

Дизассемблер файла класса javap

По общему признанию, приведенный выше неинтересный вывод генерируется утилитой javap для проверки байт-кода, сгенерированного при компиляции класса CommandTutorial ниже. Обратите внимание на то, что в классе есть множество переменных и методов с множеством модификаторов доступа. По умолчанию JDK утилита javap выводит информацию только о полях, не являющихся private, хотя флаг -private позволяет предоставить информацию о private элементах.

package com.mcnz.javap.example;
import java.util.ArrayList;

public class CommandTutorial extends ArrayList<Object> {

  private static final long serialVersionUID = 1L;
  protected static String foo;
  public String bar;

  void doStuff() { }
  private String doStringStuff() { return null; }

  public static void main(String args[]) {
    class InnerClass {
      public String stringStuff() {return "";};
    }
  }

  interface InnerType { public void singleMethod(); };

  public void useInnerType() {
    InnerType innerTypeImpl = () -> System.out.println("javap example!");
  }
}

Популярные параметры команды javap

Вывод утилиты javap по умолчанию не особенно интересен. Однако есть множество флагов, которые можно использовать для вывода значительно более интересной информации. Популярные параметры и флаги команды javap включают:

  • -l распечатает таблицы локальных переменных для класса

  • -s будет печатать внутренние типы сигнатуры класса

  • -sysinfo отобразит дату последнего обновления, хеш MD5, путь и информацию о размере класса

  • -verbose выводит обширную информацию о размере стека, количестве аргументов и локальных переменных для методов, а также большой объем информации о структуре класса.

  • -c заставляет утилиту javap распечатать инструкции байт-кода Java, эффективно дизассемблируя класс

У утилиты javap есть несколько опций. 
Команда javap с ключом sysinfo показывает хэш и информацию о последнем обновлении.
У утилиты javap есть несколько опций.  Команда javap с ключом sysinfo показывает хэш и информацию о последнем обновлении.

Как использовать команду javap

Большинство пользователей утилиты javap заинтересованы в декомпиляции класса и просмотре дизассемблированного байт-кода. Вот как использовать утилиту javap для просмотра байт-кода инструкций:

  1. Убедитесь, что утилита javap находятся по пути в переменной PATH операционной системы.

  2. Убедитесь, что класс для проверки javap скомпилирован

  3. Введите команду javap с параметром -c вместе с именем класса

  4. Просмотрите байт-код инструкций декомпилированного класса

Декомпилированный Java байт-код

Вот результат декомпиляции вышеуказанного класса с помощью инструмента Java javap.

C:\_tools\java8\javap example\bin> javap -verbose com.mcnz.javap.example.CommandTutorial

Classfile /C:/_tools/java8/javap example/bin/com/mcnz/javap/example/CommandTutorial.class
  Last modified Nov 25, 2021; size 1864 bytes
  MD5 checksum f1239fdfb14b9d5874f157242d884ea7
  Compiled from "CommandTutorial.java"
public class com.mcnz.javap.example.CommandTutorial extends java.util.ArrayList<java.lang.Object>
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #1                          // com/mcnz/javap/example/CommandTutorial
  super_class: #3                         // java/util/ArrayList
  interfaces: 0, fields: 3, methods: 6, attributes: 5
Constant pool:
   #1 = Class              #2             // com/mcnz/javap/example/CommandTutorial
   #2 = Utf8               com/mcnz/javap/example/CommandTutorial
   #3 = Class              #4             // java/util/ArrayList
   #4 = Utf8               java/util/ArrayList
   #5 = Utf8               serialVersionUID
   #6 = Utf8               J
   #7 = Utf8               ConstantValue
   #8 = Long               1l
  #10 = Utf8               foo
  #11 = Utf8               Ljava/lang/String;
  #12 = Utf8               bar
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Methodref          #3.#17         // java/util/ArrayList."<init>":()V
  #17 = NameAndType        #13:#14        // "<init>":()V
  #18 = Utf8               LineNumberTable
  #19 = Utf8               LocalVariableTable
  #20 = Utf8               this
  #21 = Utf8               Lcom/mcnz/javap/example/CommandTutorial;
  #22 = Utf8               doStuff
  #23 = Utf8               doStringStuff
  #24 = Utf8               ()Ljava/lang/String;
  #25 = Utf8               main
  #26 = Utf8               ([Ljava/lang/String;)V
  #27 = Utf8               args
  #28 = Utf8               [Ljava/lang/String;
  #29 = Utf8               useInnerType
  #30 = InvokeDynamic      #0:#31         // #0:singleMethod:()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
  #31 = NameAndType        #32:#33        // singleMethod:()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
  #32 = Utf8               singleMethod
  #33 = Utf8               ()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
  #34 = Utf8               innerTypeImpl
  #35 = Utf8               Lcom/mcnz/javap/example/CommandTutorial$InnerType;
  #36 = Utf8               lambda$0
  #37 = Fieldref           #38.#40        // java/lang/System.out:Ljava/io/PrintStream;
  #38 = Class              #39            // java/lang/System
  #39 = Utf8               java/lang/System
  #40 = NameAndType        #41:#42        // out:Ljava/io/PrintStream;
  #41 = Utf8               out
  #42 = Utf8               Ljava/io/PrintStream;
  #43 = String             #44            // javap example!
  #44 = Utf8               javap example!
  #45 = Methodref          #46.#48        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #46 = Class              #47            // java/io/PrintStream
  #47 = Utf8               java/io/PrintStream
  #48 = NameAndType        #49:#50        // println:(Ljava/lang/String;)V
  #49 = Utf8               println
  #50 = Utf8               (Ljava/lang/String;)V
  #51 = Utf8               SourceFile
  #52 = Utf8               CommandTutorial.java
  #53 = Utf8               Signature
  #54 = Utf8               Ljava/util/ArrayList<Ljava/lang/Object;>;
  #55 = Utf8               BootstrapMethods
  #56 = Methodref          #57.#59        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljav
a/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #57 = Class              #58            // java/lang/invoke/LambdaMetafactory
  #58 = Utf8               java/lang/invoke/LambdaMetafactory
  #59 = NameAndType        #60:#61        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang
/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #60 = Utf8               metafactory
  #61 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lan
g/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #62 = MethodHandle       6:#56          // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invok
e/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #63 = MethodType         #14            //  ()V
  #64 = Methodref          #1.#65         // com/mcnz/javap/example/CommandTutorial.lambda$0:()V
  #65 = NameAndType        #36:#14        // lambda$0:()V
  #66 = MethodHandle       6:#64          // REF_invokeStatic com/mcnz/javap/example/CommandTutorial.lambda$0:()V
  #67 = MethodType         #14            //  ()V
  #68 = Utf8               InnerClasses
  #69 = Class              #70            // com/mcnz/javap/example/CommandTutorial$1InnerClass
  #70 = Utf8               com/mcnz/javap/example/CommandTutorial$1InnerClass
  #71 = Utf8               InnerClass
  #72 = Class              #73            // com/mcnz/javap/example/CommandTutorial$InnerType
  #73 = Utf8               com/mcnz/javap/example/CommandTutorial$InnerType
  #74 = Utf8               InnerType
  #75 = Class              #76            // java/lang/invoke/MethodHandles$Lookup
  #76 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #77 = Class              #78            // java/lang/invoke/MethodHandles
  #78 = Utf8               java/lang/invoke/MethodHandles
  #79 = Utf8               Lookup
  #80 = Utf8               NestMembers
{
  protected static java.lang.String foo;
    descriptor: Ljava/lang/String;
    flags: (0x000c) ACC_PROTECTED, ACC_STATIC

  public java.lang.String bar;
    descriptor: Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC

  public com.mcnz.javap.example.CommandTutorial();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #16                 // Method java/util/ArrayList."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/mcnz/javap/example/CommandTutorial;

  void doStuff();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   Lcom/mcnz/javap/example/CommandTutorial;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 19: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;

  public void useInnerType();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: invokedynamic #30,  0             // InvokeDynamic #0:singleMethod:()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
         5: astore_1
         6: return
      LineNumberTable:
        line 26: 0
        line 27: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/mcnz/javap/example/CommandTutorial;
            6       1     1 innerTypeImpl   Lcom/mcnz/javap/example/CommandTutorial$InnerType;
}
SourceFile: "CommandTutorial.java"
Signature: #54                          // Ljava/util/ArrayList<Ljava/lang/Object;>;
BootstrapMethods:
  0: #62 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Metho
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;

  public void useInnerType();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: invokedynamic #30,  0             // InvokeDynamic #0:singleMethod:()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
         5: astore_1
         6: return
      LineNumberTable:
        line 26: 0
        line 27: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/mcnz/javap/example/CommandTutorial;
            6       1     1 innerTypeImpl   Lcom/mcnz/javap/example/CommandTutorial$InnerType;
}
SourceFile: "CommandTutorial.java"
Signature: #54                          // Ljava/util/ArrayList<Ljava/lang/Object;>;
BootstrapMethods:
  0: #62 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Metho
dType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #63 ()V
        line 19: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;

  public void useInnerType();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: invokedynamic #30,  0             // InvokeDynamic #0:singleMethod:()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
         5: astore_1
         6: return
      LineNumberTable:
        line 26: 0
        line 27: 6
      LocalVariableTable:

        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/mcnz/javap/example/CommandTutorial;
            6       1     1 innerTypeImpl   Lcom/mcnz/javap/example/CommandTutorial$InnerType;
}
SourceFile: "CommandTutorial.java"
Signature: #54                          // Ljava/util/ArrayList<Ljava/lang/Object;>;
BootstrapMethods:
  0: #62 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang
/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #63 ()V
      #66 REF_invokeStatic com/mcnz/javap/example/CommandTutorial.lambda$0:()V
      #67 ()V
InnerClasses:
  #71= #69;                               // InnerClass=class com/mcnz/javap/example/CommandTutorial$1InnerClass
  static #74= #72 of #1;                  // InnerType=class com/mcnz/javap/example/CommandTutorial$InnerType of class com/mcnz/javap/example
/CommandTutorial
  public static final #79= #75 of #77;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
NestMembers:
  com/mcnz/javap/example/CommandTutorial$1InnerClass
  com/mcnz/javap/example/CommandTutorial$InnerType

C:\_tools\java8\javap example\bin>javap -c com.mcnz.javap.example.CommandTutorial
Compiled from "CommandTutorial.java"
public class com.mcnz.javap.example.CommandTutorial extends java.util.ArrayList<java.lang.Object> {
  protected static java.lang.String foo;

  public java.lang.String bar;

  public com.mcnz.javap.example.CommandTutorial();
    Code:
       0: aload_0
       1: invokespecial #16                 // Method java/util/ArrayList."<init>":()V
       4: return

  void doStuff();
    Code:
       0: return

  public static void main(java.lang.String[]);
    Code:
       0: return

  public void useInnerType();
    Code:
       0: invokedynamic #30,  0             // InvokeDynamic #0:singleMethod:()Lcom/mcnz/javap/example/CommandTutorial$InnerType;
       5: astore_1
       6: return
}

Результаты выполнения команды javap являются подробными и обширными. Для тех, кому интересно взглянуть на некоторые внутренние механизмы скомпилированного байт-кода Java, утилита javap - это правильная команда.

Комментарии (0)