Объекты и массивы

Добро пожаловать в очередной выпуск Under The Hood . В этой колонке основное внимание уделяется базовым технологиям Java. Его цель - дать разработчикам представление о механизмах, которые заставляют их программы Java работать. В статье этого месяца рассматриваются байт-коды, которые имеют дело с объектами и массивами.

Объектно-ориентированная машина

Виртуальная машина Java (JVM) работает с данными в трех формах: объекты, ссылки на объекты и примитивные типы. Объекты находятся в куче со сборкой мусора. Ссылки на объекты и примитивные типы находятся либо в стеке Java как локальные переменные, либо в куче как переменные экземпляра объектов, либо в области метода как переменные класса.

В виртуальной машине Java память выделяется в куче со сборкой мусора только как объекты. Невозможно выделить память для примитивного типа в куче, кроме как как часть объекта. Если вы хотите использовать примитивный тип, где требуется Objectссылка, вы можете выделить объект-оболочку для этого типа из java.langпакета. Например, есть Integerкласс, который оборачивает intтип объектом. Только объектные ссылки и примитивные типы могут находиться в стеке Java как локальные переменные. Объекты никогда не могут находиться в стеке Java.

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

В наборе инструкций JVM все объекты создаются и доступны с одним и тем же набором кодов операций, за исключением массивов. В Java массивы представляют собой полноценные объекты и, как и любой другой объект в программе Java, создаются динамически. Ссылки на массивы могут использоваться везде, где Objectвызывается ссылка на тип , и любой метод Objectможет быть вызван для массива. Однако в виртуальной машине Java массивы обрабатываются специальными байт-кодами.

Как и любой другой объект, массивы нельзя объявлять как локальные переменные; могут только ссылки на массивы. Сами объекты массивов всегда содержат либо массив примитивных типов, либо массив ссылок на объекты. Если вы объявляете массив объектов, вы получаете массив ссылок на объекты. Сами объекты должны быть явно созданы с newэлементами массива и назначены им.

Коды операций для объектов

Создание новых объектов осуществляется через

new

код операции. Два однобайтовых операнда следуют за

new

код операции. Эти два байта объединяются для формирования 16-разрядного индекса в пуле констант. Элемент константного пула по указанному смещению дает информацию о классе нового объекта. JVM создает новый экземпляр объекта в куче и помещает ссылку на новый объект в стек, как показано ниже.

Создание объекта
Код операции Операнд (ы) Описание
new indexbyte1, indexbyte2 создает новый объект в куче, выталкивает ссылку

В следующей таблице показаны коды операций, которые помещают и получают поля объекта. Эти коды операций, putfield и getfield, работают только с полями, которые являются переменными экземпляра. Доступ к статическим переменным осуществляется методами putstatic и getstatic, которые описаны ниже. Команды putfield и getfield принимают по два однобайтовых операнда. Операнды объединяются для формирования 16-битного индекса в пуле констант. Элемент постоянного пула в этом индексе содержит информацию о типе, размере и смещении поля. Ссылка на объект берется из стека в инструкциях putfield и getfield. Инструкция putfield берет значение переменной экземпляра из стека, а инструкция getfield помещает полученное значение переменной экземпляра в стек.

Доступ к переменным экземпляра
Код операции Операнд (ы) Описание
putfield indexbyte1, indexbyte2 установить поле, указанное индексом, объекта в значение (оба взяты из стека)
getfield indexbyte1, indexbyte2 выталкивает поле, указанное индексом, объекта (берется из стека)

Доступ к переменным класса осуществляется через коды операций getstatic и putstatic, как показано в таблице ниже. И getstatic, и putstatic принимают два однобайтовых операнда, которые объединяются JVM для формирования 16-битного смещения без знака в пуле констант. Элемент постоянного пула в этом месте дает информацию об одном статическом поле класса. Поскольку нет конкретного объекта, связанного со статическим полем, нет ссылки на объект, используемой ни getstatic, ни putstatic. Инструкция putstatic принимает значение для назначения из стека. Инструкция getstatic помещает полученное значение в стек.

Доступ к переменным класса
Код операции Операнд (ы) Описание
putstatic indexbyte1, indexbyte2 установить поле, указанное индексом, объекта в значение (оба взяты из стека)
getstatic indexbyte1, indexbyte2 выталкивает поле, указанное индексом, объекта (берется из стека)

Следующие ниже коды операций проверяют, ссылается ли ссылка на объект в верхней части стека на экземпляр класса или интерфейса, индексированный операндами, следующими за кодом операции. Инструкция checkcast выдается, CheckCastExceptionесли объект не является экземпляром указанного класса или интерфейса. В противном случае checkcast ничего не делает. Ссылка на объект остается в стеке, и выполнение продолжается со следующей инструкции. Эта инструкция обеспечивает безопасность приведения во время выполнения и является частью защитной оболочки JVM.

Инструкция instanceof выталкивает ссылку на объект из вершины стека и выдвигает true или false. Если объект действительно является экземпляром указанного класса или интерфейса, тогда в стек помещается значение true, в противном случае в стек помещается значение false. Инструкция instanceof используется для реализации instanceofключевого слова Java, которое позволяет программистам проверять, является ли объект экземпляром определенного класса или интерфейса.

Проверка типа
Код операции Операнд (ы) Описание
checkcast indexbyte1, indexbyte2 Выбрасывает ClassCastException, если ссылка на объект в стеке не может быть преобразована в класс по индексу
instanceof indexbyte1, indexbyte2 Выдвигает истину, если ссылка на объект в стеке является классом instanceof по индексу, иначе нажимает ложь

Коды операций для массивов

Создание новых массивов осуществляется с помощью кодов операций newarray, anewarray и multianewarray. Код операции newarray используется для создания массивов примитивных типов, отличных от объектных ссылок. Конкретный тип примитива определяется одним однобайтовым операндом, следующим за кодом операции newarray. Инструкция newarray может создавать массивы для byte, short, char, int, long, float, double или boolean.

Инструкция anewarray создает массив ссылок на объекты. Два однобайтовых операнда следуют за кодом операции нового массива и объединяются для формирования 16-битного индекса в пуле констант. Описание класса объекта, для которого должен быть создан массив, находится в пуле констант по указанному индексу. Эта инструкция выделяет место для массива ссылок на объекты и инициализирует ссылки нулевым значением.

The multianewarray instruction is used to allocate multidimensional arrays -- which are simply arrays of arrays -- and could be allocated with repeated use of the anewarray and newarray instructions. The multianewarray instruction simply compresses the bytecodes needed to create multidimensional arrays into one instruction. Two one-byte operands follow the multianewarray opcode and are combined to form a 16-bit index into the constant pool. A description of the class of object for which the array is to be created is found in the constant pool at the specified index. Immediately following the two one-byte operands that form the constant pool index is a one-byte operand that specifies the number of dimensions in this multidimensional array. The sizes for each dimension are popped off the stack. This instruction allocates space for all arrays that are needed to implement the multidimensional arrays.

Creating new arrays
Opcode Operand(s) Description
newarray atype pops length, allocates new array of primitive types of type indicated by atype, pushes objectref of new array
anewarray indexbyte1, indexbyte2 pops length, allocates a new array of objects of class indicated by indexbyte1 and indexbyte2, pushes objectref of new array
multianewarray indexbyte1, indexbyte2, dimensions pops dimensions number of array lengths, allocates a new multidimensional array of class indicated by indexbyte1 and indexbyte2, pushes objectref of new array

The next table shows the instruction that pops an array reference off the top of the stack and pushes the length of that array.

Getting the array length
Opcode Operand(s) Description
arraylength (none) pops objectref of an array, pushes length of that array

The following opcodes retrieve an element from an array. The array index and array reference are popped from the stack, and the value at the specified index of the specified array is pushed back onto the stack.

Retrieving an array element
Opcode Operand(s) Description
baload (none) pops index and arrayref of an array of bytes, pushes arrayref[index]
caload (none) pops index and arrayref of an array of chars, pushes arrayref[index]
saload (none) pops index and arrayref of an array of shorts, pushes arrayref[index]
iaload (none) pops index and arrayref of an array of ints, pushes arrayref[index]
laload (none) pops index and arrayref of an array of longs, pushes arrayref[index]
faload (none) pops index and arrayref of an array of floats, pushes arrayref[index]
daload (none) pops index and arrayref of an array of doubles, pushes arrayref[index]
aaload (none) pops index and arrayref of an array of objectrefs, pushes arrayref[index]

The next table shows the opcodes that store a value into an array element. The value, index, and array reference are popped from the top of the stack.

Storing to an array element
Opcode Operand(s) Description
bastore (none) pops value, index, and arrayref of an array of bytes, assigns arrayref[index] = value
castore (none) pops value, index, and arrayref of an array of chars, assigns arrayref[index] = value
sastore (none) pops value, index, and arrayref of an array of shorts, assigns arrayref[index] = value
iastore (none) pops value, index, and arrayref of an array of ints, assigns arrayref[index] = value
lastore (none) pops value, index, and arrayref of an array of longs, assigns arrayref[index] = value
fastore (none) pops value, index, and arrayref of an array of floats, assigns arrayref[index] = value
dastore (none) pops value, index, and arrayref of an array of doubles, assigns arrayref[index] = value
aastore (none) извлекает значение, индекс и ссылку на массив из массива ссылок на объекты, присваивает ссылку на массив [индекс] = значение

Трехмерный массив: симуляция виртуальной машины Java

Апплет ниже демонстрирует, как виртуальная машина Java выполняет последовательность байт-кодов. Последовательность байт-кода в моделировании была сгенерирована javacдля initAnArray()метода класса, показанного ниже:

class ArrayDemo {static void initAnArray () {int [] [] [] threeD = new int [5] [4] [3]; for (int i = 0; i <5; ++ i) {for (int j = 0; j <4; ++ j) {for (int k = 0; k <3; ++ k) {threeD [ я] [j] [k] = i + j + k; }}}}}

Байт-коды, созданные javacдля initAnArray(), показаны ниже: