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 заинтересованы в декомпиляции класса и просмотре дизассемблированного байт-кода. Вот как использовать утилиту javap для просмотра байт-кода инструкций:
Убедитесь, что утилита javap находятся по пути в переменной PATH операционной системы.
Убедитесь, что класс для проверки javap скомпилирован
Введите команду javap с параметром -c вместе с именем класса
Просмотрите байт-код инструкций декомпилированного класса
Декомпилированный 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 - это правильная команда.