Практика разбора файла класса

задняя часть

Реализация кроссплатформенности Java основана на виртуальной машине JVM.Написанный исходный код Java создаст.classфайл, называемый файлом байт-кода. Виртуальная машина Java отвечает за преобразование файла байт-кода в машинный код на определенной платформе и его последующее выполнение. Чтобы обеспечить универсальность файлов Class на нескольких платформах, в Java официально сформулирован строгий формат файлов Class. Понимание структуры файла класса поможет нам декомпилировать.classфайл или изменить байт-код во время компиляции программы для внедрения кода.

Обзор структуры файла класса

Сначала создайте класс Java:

public class HelloWorld {

	private static int num = 0;
	public String name = "HelloWorld";

	public static void main(String[] args) {

		String[] strs = {"bigkai1", "bigkai2"};

		for (int i = 0; i < 10; i++) {
			num++;
			if(i == 5) continue;
			System.out.println("HelloWorld!");
		}
	}
}

Затем перейдите в текущий каталог класса и выполнитеjavacКоманда для создания файла класса:

$ javac HelloWorld.java

Мы видим, что файл java создается подHelloWorld.classфайл, используя файловый анализаторclasspyОткройте файл, и вы увидите общую структуру файла:

1593046004146

Общая структура файла класса:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

Я сделал простую схему структуры файла класса:

1592986196457

В JVM файл Class описывается на C-подобном языке, а в качестве базового типа данных единообразно используются целые числа без знака: однобайтовыеu1, 2 байтаu2, 4 байтаu4, 8 байтu8.

В следующих разделах анализируется каждая часть файла.

магическое число

Magic Number — это идентификатор файла класса, это 4-байтовое целое число, только текущие четыре байта0xCAFEBABE(вспоминается как咖啡宝贝английский перевод), виртуальная машина будет думать, что это файл класса. Эта практика запуска с фиксированным идентификатором использовалась во многих местах, таких какzip的压缩文件.

Проверьте наш файл класса для этого идентификатора:

1593046021503

когда я искусственноCA FE BA BEпревратиться вCA FE BA BAКогда виртуальная машина загружает файл класса, виртуальная машина выдает следующую ошибку при проверке файла:

1593002106582

номер версии

За магическим номером стоит номер версии класса, который имеет два типа: дополнительный номер версии (minor_version) и основной номер версии (major_version). Они объединяются, чтобы указать, с какой версией JDK скомпилирован текущий файл класса. Ниже приведена карта версий, взятая с официального сайта java:

1593046150870

Сравнивая этот рисунок, мы можем просмотреть соответствующую версию jdk через номер версии:

1593046055854

В моем файле класса номер версии0x0037, преобразованный в десятичный как55, что соответствует jdk11.

Для файлов классов с основной_версией 56 или выше, минорная_версия должна быть 0 или 65535.

Для файлов классов с основной_версией от 45 до 55 минорная_версия может иметь любое значение.

Когда я искусственно меняю основной номер версии на0x0039, то есть соответствующий версии jdk14, а затем загрузить файл класса.Так как моя версия jdk 11, виртуальная машина может быть только обратно совместимой, поэтому будет выдано сообщение об ошибке:

1593002837588

постоянный пул

Пул констант является одним из наиболее важных компонентов содержимого в файле класса. Пул констант грубо разделен на статические пулы констант и пулы констант времени выполнения. Пул статических констант хранится в файле класса, а пул констант времени выполнения ссылается к загрузке файла класса в контент, после чего сохраняется область метода константного пула. Здесь мы анализируем статический пул констант.

Формат каждой записи в статическом пуле констант:

cp_info {
    u1 tag;
    u1 info[];
}

Тег указывает тип константы, которую представляет запись. Есть 17 констант:

1593003638021

Я анализирую первый элемент постоянного пула сгенерированного файла класса:

1593046191758

видно, чтоtagда0A, согласно приведенной выше таблице этоCONSTANT_Methodref, структура такая:

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

то согласно следующему0x000C, предполагаемыйclass_indexПункт 12 в постоянном пуле

class_indexЗначение представляет собой индекс в пуле констант, представляющий класс или тип интерфейса, который имеет поля или методы в качестве членов.

  • В структуре CONSTANT_Fieldref_info элемент class_index может быть типом класса или типом интерфейса.
  • В структуре CONSTANT_Methodref_info элемент class_index должен быть типом класса, а не типом интерфейса.
  • В структуре CONSTANT_InterfaceMethodref_info элемент class_index должен быть типом интерфейса, а не типом класса.

Затем прочитайте два байта назад0x001C, который представляет имя поля или метода в пуле констант и значение индекса дескриптора.

мы начинаем сclass_indexПосмотрите, в каком классе он находится:

1593046277317

могу это увидетьtagравно 7, что указывает на то, чтоCONOSTANT_CLASS, структура такая:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

этоname_indexУказывает имя класса, посмотрим0x0028Соответствующий пункт:

1593046312094

Видно, что этоCONSTANT_Utf8, структура такая:

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

Его длина0x0010=16, поэтому он был прочитан через 16 байт в будущем, и его имя:java/lang/Object.

Тогда мы смотрим на этоname_and_type_indexуказывает на, что соответствует записи 28:

1593046347037

tag=12 означает, что этоCONSTANT_NameAndTypeТип, структура:

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

name_indexМы уже знаем, что это значит, давайте сразу к этому.descriptor_index, который используется для представления действительного дескриптора поля или дескриптора метода:

1593046375605

Дескрипторы полей, соответствующие разным буквам:

1593005698366

Для дескрипторов методов он имеет дескрипторы параметров и дескрипторы возврата, а для дескрипторов возврата он просто добавляетV, что соответствует возвращаемому значениюvoid.

токен доступа к классу

За пулом констант находится токен доступа к классу, который представляет собойu2Байт типа, представляющий информацию о доступе к классу, и модификаторы доступа к отображению следующие:

1593006042082

Представление каждого типа представлено установкой определенного бита в флаге доступа.Из рисунка видно, что мой файл класса0x0021:

1593046463720

Тогда вы можете знать, что модификатор доступа классаACC_PUBLIC|ACC_SUPER(0x0021=0x0020+x0001).

информация об отношениях классов

После тега доступа указывается класс классаthis_class, категория родительского класса (родительским классом верхнего уровня всех классов являетсяObject)super_classи количество реализованных интерфейсовinterface_count, категория интерфейсаinterface_index.

Проверьте мой файл класса:

1593046497319

Класс этого класса — 11, родительский класс — 12, количество интерфейсов — 0, а затем соответствующая информация ищется в пуле констант по индексу:

1593046548310

видите, ониCONSTANT_ClassТипа, смотрим их имена исходя из двух последних байтов:

1593046570556

1593046586677

они всеCONSTANT_Utf8Тип, после прочтения по соответствующей структуре, соответственноHelloWorld,java/lang/Object. Видно, что имя классаHelloWorld, его родительский классjava.lang.Object, который не реализует интерфейс.

Информация о поле

После информации о классе идет информация о поле, которая делится на количество полей (fields_count) и таблица полей (fields_info), количество полей равно одномуu2Типа, в основном посмотрите на структуру таблицы информации поля:

field_info {
    u2             access_flags; // 字段访问标记
    u2             name_index; // 字段名
    u2             descriptor_index; // 描述符
    u2             attributes_count; // 字段属性数量
    attribute_info attributes[attributes_count]; // 字段属性表 
}

токен доступа к полю: Подобно тегу доступа класса, метод расчета аналогичен методу класса.

1593046907462

имя поля: указывает на постоянный индекс пула.

Дескриптор: используется для описания типа поля, указывающего на постоянный индекс пула, типы полей:

1593046982725

Количество атрибутов поля: Запишите количество атрибутов поля, атрибуты — это дополнительная информация о поле, такая как значения инициализации, комментарии и т. д.

Свойства поля: сохранить конкретное содержимое атрибута.

Возьмите мой сгенерированный файл класса в качестве примера:

1593047131318

Всего есть два поля, байт-код первого поля выражается как00 0A 00 0D 0E 00 00, с доступом, отмеченным какACC_PRIVATE | ACC_STATIC(00 0A = 00 02 + 00 08), имя поля00 0D, дескриптор00 0E, количество атрибутов00 00.

Содержимое таблицы атрибутов описано в методе.

метод

Метод файла Class состоит из двух частей: количества методов и содержимого метода.u2Введите данные, за которыми следует информация о методе Структура информации о методе:

method_info {
    u2             access_flags; // 访问标记
    u2             name_index; // 方法名
    u2             descriptor_index; // 描述符
    u2             attributes_count; // 属性数量
    attribute_info attributes[attributes_count]; // 属性内容
}

Токенов доступа для методов гораздо больше, чем для полей:

1593047758176

name_indexиндекс имени метода,descriptor_indexУказывает сигнатуру метода (параметры, возвращаемые значения и т. д.), а дескриптор метода представлен в пуле констант как(参数1参数2)返回值.

Основная проблемаattribute_info, его структура:

attribute_info {
    u2 attribute_name_index; // 属性名
    u4 attribute_length; // 属性长度
    u1 info[attribute_length]; // 属性
}

Существуют различные свойства:

1593048181717

Краткий обзор часто используемых свойств:

Для следующих свойств некоторые не требуются во время выполнения и могут использоваться отдельно в Javac.-g : noneили-g :varsвозможность отменить или запросить генерацию этой информации.

Code

Атрибут Code хранит байт-код и другую информацию о методе и является телом выполнения метода Структура атрибута Code:

Code_attribute {
    u2 attribute_name_index; // 属性名——固定为Code
    u4 attribute_length; // 属性长度(不包括前面6个字节)
    u2 max_stack; // 操作数栈最大深度
    u2 max_locals; // 局部变量最大个数
    u4 code_length; // 方法字节码长度
    u1 code[code_length]; // 字节码内容
    u2 exception_table_length; // 异常处理表长度
    /*
    从方法字节码的start_pc偏移量开始到end_pc偏移量为止的代码中,如果遇到了catch_type所指定的异常,那么代码就跳转到handler_pc位置执行。
    */
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length]; // 异常处理表内容
    u2 attributes_count; // 属性个数
    attribute_info attributes[attributes_count]; // 属性内容
}

ConstantValue

Роль свойства ConstantValue заключается в том, чтобы уведомить виртуальную машину об автоматическом присвоении значений статическим переменным. только поstaticЭто свойство доступно только для переменных с измененным ключевым словом (переменных класса). Его структура:

ConstantValue_attribute {
    u2 attribute_name_index; // 固定ConstantValue
    u4 attribute_length; // 固定2
    u2 constantvalue_index; // 常量池的有效索引
}

если вfield_infoструктурныйaccess_flagsэлемент установленACC_STATICподпишись, тогдаfield_infoПоля, представленные структурой, будут присвоены ееConstantValueЗначение, представленное свойством как часть инициализации класса или интерфейса, в котором объявлено поле, которое происходит до вызова метода инициализации класса или интерфейса класса или интерфейса.

Signature

Подпись была выпущена в JDK1.5 и появилась в таблице свойств класса, таблице свойств и структуре таблицы методов. Если универсальная сигнатура любого класса, интерфейса, метода инициализации или члена содержит переменные типа (переменные типа) или параметризованные типы (параметризованные типы), свойство Signature запишет для него информацию об универсальной сигнатуре. Его структура:

Signature_attribute {
    u2 attribute_name_index; // 固定Signature
    u4 attribute_length; // 固定2
    
    /*
    如果该签名属性是类文件结构的属性,则该索引处的常量池项必须是表示类签名的常量信息结构);
    如果该签名属性是方法信息结构的属性,则必须是方法签名;否则,必须是字段签名。
    */
    u2 signature_index; // 常量池有效索引。
}

Причина, по которой такой атрибут специально используется для записи универсальных типов, заключается в том, что универсальные типы языка Java принимают псевдоуниверсальные типы, реализованные методом стирания. ) все потом стираются. Преимущество использования метода стирания в том, что его легко реализовать (основная модификацияJavacКомпилятор, внутри виртуалки делается всего несколько изменений), очень прост в реализацииBackport, среда выполнения также может экономить место в памяти, занимаемое некоторыми типами. Но недостатком является то, что среда выполнения не может обрабатывать универсальные типы и определяемые пользователем обычные типы так же, как языки, поддерживающие настоящие дженерики, такие как C # Например, универсальная информация не может быть получена во время отражения во время выполнения.SignatureАтрибуты добавлены, чтобы восполнить этот недостаток.Теперь API отражения Java может получать общие типы, и конечным источником данных является этот атрибут.

LineNumberTable

LineNumberTableОн используется для записи соответствия между смещением байт-кода и номером строки. Это не обязательный атрибут во время выполнения, но по умолчанию создается в файле класса. Если вы решите не генерироватьLineNumberTableАтрибут, основное влияние на работу программы заключается в том, что при выдаче исключения номер строки ошибки не будет отображаться в стеке, а при отладке программы точка останова не может быть установлена ​​по исходной строке.LineNumberTableСтруктура имущества такова:

LineNumberTable_attribute {
    u2 attribute_name_index; // 固定为LineNumberTable
    u4 attribute_length; // 属性长度
    u2 line_number_table_length; // 表项长度
    {   u2 start_pc; // 字节码偏移量
        u2 line_number;	 // 行号
    } line_number_table[line_number_table_length]; // 表项内容
}

LocalVariableTable

LocalVariableTableАтрибут представляет собой таблицу локальных переменных, а не обязательный атрибут во время выполнения, но он будет сгенерирован в файл класса по умолчанию.Если этот атрибут не сгенерирован, когда другие люди будут ссылаться на этот метод, все имена параметров будут потеряны, и IDE будет использовать такие, какarg0,arg1заполнители, такие как исходное имя параметра. Его структура выглядит следующим образом:

LocalVariableTable_attribute {
    u2 attribute_name_index; // 固定LocalVariableTable
    u4 attribute_length; // 表项长度
    u2 local_variable_table_length;
    {   u2 start_pc; // 字节码偏移量
        u2 length; // 长度
        u2 name_index; // 局部变量名
        u2 descriptor_index; // 局部变量描述符
        u2 index; // 局部变量在当前栈帧的局部变量表中的槽位
    } local_variable_table[local_variable_table_length]; // 表项内容
}

StackMapTable

Это атрибут, введенный JDK1.6, расположенный вCodeТаблица атрибутов атрибута, интерфейс имеет данные нескольких фреймов карты стека, этот атрибут не требуется во время выполнения и используется только для проверки типа класса. Он будет использоваться новым средством проверки типов на этапе проверки байт-кода при загрузке класса виртуальной машины и предназначен для замены предыдущего средства проверки вывода типа на основе анализа потока данных, который был относительно ресурсоемким. Его структура выглядит следующим образом:

StackMapTable_attribute {
    u2              attribute_name_index; // 固定StackMapTable
    u4              attribute_length; // 表项长度
    u2              number_of_entries; // 栈映射帧属性
    stack_map_frame entries[number_of_entries]; // 栈映射帧具体内容
}

stack_map_frameСтруктура:

union stack_map_frame {
    /*
    same_frame {
    	u1 frame_type = SAME; // 0-63
	}
    */
    same_frame; // 表示当前代码所在位置和上一个比较位置的局部变量表是否相同,并且操作数栈为空
    
    /*
    same_locals_1_stack_item_frame {
        u1 frame_type = SAME_LOCALS_1_STACK_ITEM; // 64-127
        verification_type_info stack[1];
	}
    */
    same_locals_1_stack_item_frame; // 表示当前帧和上一帧有相同的局部变量,并且操作数栈中变量的数量为1
    
    /*
    same_locals_1_stack_item_frame_extended {
        u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; // 247-
        u2 offset_delta;
        verification_type_info stack[1];
	}
    */
    same_locals_1_stack_item_frame_extended; // 表示当前帧和上一帧有相同的局部变量,操作数栈中变量的数量为1,并且offset_delta超过same_locals_1_stack_item_frame
    
    /*
    chop_frame {
        u1 frame_type = CHOP; // 248-250
        u2 offset_delta;
	}
    */
    chop_frame; // 表示操作数栈为空,当前局部变量表比前一帧少K(K=2510frrame_type)个局部变量
    
    /*
    same_frame_extended {
        u1 frame_type = SAME_FRAME_EXTENDED; // 251-
        u2 offset_delta;
	}
    */
    same_frame_extended; // 表示当前代码所在位置和上一个比较位置的局部变量表是否相同,并且操作数栈为空,支持的offset_delta更大
    
    /*
    append_frame {
        u1 frame_type = APPEND; // 252-254
        u2 offset_delta;
        verification_type_info locals[frame_type - 251];
	}
    */
    append_frame; // 表示当前帧比上一帧多了K(K=frame_type-251)个局部变量,且操作数栈为空
    
    /*
    full_frame {
        u1 frame_type = FULL_FRAME; // 255
        u2 offset_delta;
        u2 number_of_locals; // 局部变量表的数量
        verification_type_info locals[number_of_locals]; // 局部变量表的数据类型
        u2 number_of_stack_items; // 操作数栈的数量
        verification_type_info stack[number_of_stack_items]; // 操作数栈的类型
	}
    */
    full_frame; // 完整记录了局部变量表和操作数栈
}

Exceptions

В дополнение к атрибуту Code каждый метод может иметьExceptionsСвойство, содержащее исключения, которые могут быть вызваны этим методом. Он устроен следующим образом:

Exceptions_attribute {
    u2 attribute_name_index; // 固定为Exceptions
    u4 attribute_length; // 属性长度
    u2 number_of_exceptions; // 表项数量,可能抛出的异常数
    u2 exception_index_table[number_of_exceptions]; // 存储了所有异常,每一项为执行常量池的一个索引 
}

Примечание. Исключения метода представляют собой исключения, которые может генерировать метод, обычноthrowsуказано ключевое слово, аCodeТаблица исключений внутри — это механизм обработки исключений.try-catchсозданное заявление.

Метод анализа байт-кода

Проведите простой анализ части метода сгенерированного мной файла класса:

1593062393840

заmainметод, вы можете видеть, что его идентификатор доступа00 09(ACC_STATIC | ACC__PUBLIC), name_index соответствует постоянному пулу00 15, дескриптор соответствует константному пулу00 16, количество атрибутов00 01, затем посмотрите на лист его свойств:

1593062545753

Его можно сравнивать один за другим по предыдущему содержанию Максимальное количество стеков операций — 4, максимальное количество локальных переменных — 54, а длина в методе — 54.codeВ нем хранятся инструкции для выполнения виртуальной машиной. Ниже мы в основном сосредоточимся на ееcodeПрежде чем анализировать содержимое, вы должны сначала понять некоторые инструкции в виртуальной машине.

инструкция

Существует множество наборов инструкций JVM, которые можно условно разделить на:

  • серия const: отвечает за размещениепростые числовые типына вершину стека. Например, соответствующий тип int можно использовать только таким образом.-1,0,1,2,3,4,5Нажмите на вершину стека.Для типа int используйте серию команд push для других значений.
  • серия push: эта серия команд отвечает за отправку целого числа (меньшей длины) на вершину стека. Он принимает аргумент, указывающий число, которое должно быть отправлено на вершину стека, а команда ldc будет использоваться для данных вне допустимого диапазона.
  • Серия ldc: эта серия команд отвечает за перемещение числовой константы или значения строковой константы из пула констант в верхнюю часть стека. За этой командой должен следовать параметр, указывающий позицию (номер) константы в пуле констант.对于const系列命令和push系列命令操作范围之外的数值类型常量,以及所有不是通过new创建的String,都放在常量池中。
  • серия нагрузки:
    • Серия loadA: отвечает за отправку локальных переменных на вершину стека. Локальные переменные здесь могут быть не только числового типа, но и ссылочного типа.
    • Серия loadB: отвечает за отправку элемента массива на вершину стека. Эта команда определяет, с каким элементом какого массива работать, в соответствии с содержимым стека.
  • Серия магазина:
    • Серия storeA: отвечает за сохранение значения наверху стека в локальную переменную. Локальные переменные здесь могут быть не только числового типа, но и ссылочного типа.
    • Серия storeB: отвечает за сохранение значения верхнего элемента стека в массив. Эта команда определяет, с каким элементом какого массива работать, в соответствии с содержимым стека.
  • серия pop: извлеките верхний элемент стека (или назначьте и поместите верхний элемент стека).
  • Серия преобразования типов: Эта инструкция предназначена для преобразования типов, мнемоника для этого типа инструкций представлена ​​в виде x2y. где х может бытьi,f,l,d,yможет бытьi,f,l,d,c,s,b.
  • Серия операций: предоставляет базовые операции сложения, вычитания, умножения и деления для виртуальных машин.
  • Серии массивов: Инструкции по работе с объектами можно дополнительно подразделить на инструкции по созданию, инструкции по доступу к полям, инструкции по проверке типов и инструкции по работе с массивами.
  • Контрольная серия: означает условное управление. Грубо разделены на инструкции сравнения, инструкции условного перехода, инструкции сравнения условного перехода, переходы с несколькими условными переходами, инструкции безусловного перехода и т. д.
  • Серия функций: включая инструкции по вызову функции и инструкции по возврату функции.
  • Последовательность управления синхронизацией: виртуальная машина Java предоставляет функции monitorenter и monitorexit для завершения входа и выхода из критических разделов. добиться многопоточной синхронизации.

Конкретные команды набора инструкций можно просмотреть в этом блоге:Завершение набора инструкций CSDN JVM.

Анализ байт-кода инструкций

После понимания инструкций, связанных с байт-кодом, давайте сразимся со сгенерированным файлом класса:

1593132588982

Фактический код:

private static int num = 0;

public static void main(String[] args) {

		String[] strs = {"bigkai1", "bigkai2"};

		for (int i = 0; i < 10; i++) {
			num++;
			if(i == 5) continue;
			System.out.println("HelloWorld!");
		}
	}

первое использованиеiconst_2депозитintвведите 2 в верхнюю часть стека, затемanewarrayЧтобы создать ссылку на массив и поместить ее на вершину стека (верхний элемент извлекается как длина массива), используйтеdupСкопируйте значение на вершину стека и поместите скопированное значение на вершину стека, затемiconst_0будетintТип 0 помещается в стек, используйтеldcбудетStringПостоянное значение типа помещается из пула констант на вершину стека, где оно указывает на05——bigkai1,передачаaastoreСохраните опорное значение в верхней части стека в указанной позиции индекса указанного массива.Оно основано на опорном значении в верхней части стека, нижнем индексе массива, ссылке массива и значении хранится в соответствующем элементе массива. В этот момент первый элементbigkai1хранить в массиве строкstrs.

Тогда позвониdupскопируйте верхний элемент стека и снова поместите его на вершину стека, затемiconst_1затолкнуть вintТип 1,ldcвзято из постоянного пулаbigkai2, затем позвонитеaastoreВытащите два значения из стека и присвойте второму элементу массиваbigkai2, все присваивания массиву строк на этом завершаются.

затем введитеforметод,iconst_0затолкнуть вintвведите 0, затемistore_2положить вершину стекаintЗначение типа хранится во второй локальной переменной,iload_2поставить 2-йintлокальные переменные типа помещаются на вершину стека,bipushПреобразование однобайтового постоянного значения (-128~127) помещается на вершину стека,if_icmpgeСравните размер двух значений int на вершине стека и перейдите к 53-й инструкции, когда результат больше или равен 0 (53-е выполнение —return, то есть завершить функцию, вернуть возвращаемое значение), а затем вызватьgetstaticПолучить статическое поле указанного класса (полученное из пула константnum) и поместите его значение на вершину стека,iconst_1затолкнуть вintТип 1.iaddДобавьте два значения int на вершину стека и поместите результат на вершину стека, затемputstaticзаnumзадание, в это времяnum++Законченный.

iload_2поставить 2-йintпомещает локальную переменную типа на вершину стека (помещаетi=0толкать), затемiconst_5Нажмите 5, используйтеif_icmpneСравните размер двух значений int на вершине стека, перейдите к 39-й инструкции, когда результат не равен 0, иначе вызовитеgotoПерейти к инструкции 47. Директива 47iinc, который будет указанintЧтобы добавить указанное значение к переменной типа, требуются две переменные, представляющиеindex,const,indexотносится к первомуindexиндивидуальныйintвведите локальную переменную,constпредставляет увеличенное значение, а затемgotoк Директиве 17. вот реализацияif(i == 5) continue.

Если 33-я Директиваif_icmpneЕсли он не равен 0, происходит переход к 39-й инструкции, а 39-я инструкцияgetstatic, он получает постоянный пулjava/lang/System.out, затем выполнитеldc,будетHelloWorld!нажать на вершину стека, вызватьinvokevirtualИнструкция, его роль заключается в вызове методов экземпляра, диспетчеризации в соответствии с фактическим типом объекта и поддержке полиморфизма. вот реализацияSystem.out.println("HelloWorld!").

Свойства файла класса

Файл класса также поставляется с некоторыми атрибутами, которые состоят из длины атрибута и содержимого атрибута.Основные атрибуты:

SourceFile

SourceFileАтрибут используется для описания того, из какого файла исходного кода скомпилирован текущий файл класса.

SourceFile_attribute {
    u2 attribute_name_index; // 固定SourceFile
    u4 attribute_length; // 属性长度,固定为2
    u2 sourcefile_index; // 源代码文件名,指向常量池索引
}

BootstrapMethods

BootstrapMethodsсвойства используются для поддержкиinvokeDynamicДиректива, описывающая и сохраняющая метод начальной загрузки.

invokeDynamic — это инструкция, которую JDK1.7 поддерживает для разработки языков с динамической типизацией.Так называемый язык с динамической типизацией означает, что основной процесс проверки его типов происходит во время выполнения, а не во время компиляции.Типичный репрезентативный языкPython.

Метод начальной загрузки можно просто понимать как метод поиска метода.

BootstrapMethods_attribute {
    u2 attribute_name_index; // 固定BootstrapMethods
    u4 attribute_length; // 属性总长度(不包含前6个字节)
    u2 num_bootstrap_methods; // 这个类中抱哈的引导方法的个数
    {   u2 bootstrap_method_ref; // 指明函数
        u2 num_bootstrap_arguments; // 指明引导方法的参数个数
        u2 bootstrap_arguments[num_bootstrap_arguments]; // 引导方法的参数
    } bootstrap_methods[num_bootstrap_methods];
}

InnerClasses

Он используется для описания отношений между внешними и внутренними классами:

InnerClasses_attribute {
    u2 attribute_name_index; // 固定InnerClasses
    u4 attribute_length; // 属性长度
    u2 number_of_classes; // 内部类格式
    {   u2 inner_class_info_index; // 内部类类型
        u2 outer_class_info_index; // 外部类类型
        u2 inner_name_index; // 内部类名称
        u2 inner_class_access_flags; // 内部类访问标识符
    } classes[number_of_classes]; // 内部类内容
}

Идентификаторы доступа для внутренних классов поддерживают следующее:

1593064751416

Deprecated

DeprecatedМожет использоваться в таких структурах, как классы, методы и поля, указывая на то, что класс, метод и поле будут объявлены устаревшими в будущих версиях. Его структура выглядит следующим образом:

Deprecated_attribute {
    u2 attribute_name_index; // 固定Deprecated
    u4 attribute_length; // 固定为0
}

Когда класс, метод или поле помечены какDeprecated, это свойство будет сгенерировано.

Суммировать

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