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 - это правильная команда.
