Как посмотреть байткод Java в человекочитаемом виде
Что проверяют
Знаете ли вы стандартные инструменты JDK и отличаете ли дизассемблер от декомпилятора. Следом часто просят показать, во что компилируется конкретная конструкция: конкатенация строк, лямбда, switch по строке.
Как отвечать
Основной инструмент это javap из JDK. Он работает с .class, а не с исходником:
javap -c Foo: дизассемблированный байткод методов, инструкции вродеaload_0,invokevirtual,getstatic. Без-cвы увидите только объявления;-v: подробный вывод: пул констант,max_stackиmax_locals, флаги доступа, версия class-файла;-p: добавить приватные члены, по умолчанию их нет в выводе;-l: таблицы номеров строк и локальных переменных, если класс собран с отладочной информацией;-s: внутренние дескрипторы типов, например(Ljava/lang/String;)V;- классы из jar и JDK:
javap -c -cp app.jar com.example.Fooилиjavap -c java.lang.String.
В IntelliJ IDEA то же самое открывается через меню View, пункт Show Bytecode, а для разбора пула констант и атрибутов удобен jclasslib. Декомпиляторы (встроенный в IDEA Fernflower, CFR, Vineflower) делают другое: восстанавливают Java-код, а не показывают инструкции.
Хороший пример для ответа: с Java 9 конкатенация строк компилируется не в StringBuilder, а в invokedynamic через StringConcatFactory. Лямбды тоже идут через invokedynamic и LambdaMetafactory. Всё это сразу видно в выводе javap -c.
Где спотыкаются
Называют декомпилятор ответом на вопрос про байткод. Восстановленный Java-код прячет ровно то, ради чего байткод смотрят: реальные инструкции и вызовы.
Запускают javap без -c и удивляются, что кода нет, или без -p и не находят приватный метод.
Считают, что байткод показывает, как код исполняется на самом деле. JIT-компилятор C2 может встроить вызовы и убрать проверки, а итоговый машинный код смотрят уже иначе: флагом -XX:+PrintAssembly вместе с -XX:+UnlockDiagnosticVMOptions и библиотекой hsdis.