Programování

Objekty a pole

Vítejte v dalším vydání Under The Hood. Tento sloupec se zaměřuje na základní technologie Java. Jejím cílem je poskytnout vývojářům pohled na mechanismy, které zajišťují běh jejich programů Java. Článek z tohoto měsíce pojednává o bytových kódech, které se zabývají objekty a poli.

Objektově orientovaný stroj

Virtuální stroj Java (JVM) pracuje s daty ve třech formách: objekty, odkazy na objekty a primitivní typy. Objekty jsou umístěny na hromadě odpadu. Odkazy na objekty a primitivní typy jsou umístěny buď na zásobníku Java jako lokální proměnné, na hromadě jako proměnné instance objektů, nebo v oblasti metody jako proměnné třídy.

Ve virtuálním stroji Java je paměť přidělena na haldě shromážděné odpadky pouze jako objekty. Neexistuje žádný způsob, jak na haldě přidělit paměť primitivnímu typu, kromě části objektu. Pokud chcete použít primitivní typ, kde Objekt je potřeba odkaz, můžete přidělit obalový objekt pro typ z java.lang balík. Například existuje Celé číslo třída, která obaluje int zadejte s objektem. Jako lokální proměnné mohou být v zásobníku Java umístěny pouze odkazy na objekty a primitivní typy. Objekty se nikdy nemohou nacházet v zásobníku Java.

Architektonické oddělení objektů a primitivních typů v JVM se odráží v programovacím jazyce Java, ve kterém nelze objekty deklarovat jako lokální proměnné. Jako takové lze deklarovat pouze odkazy na objekty. Po deklaraci odkaz na objekt neodkazuje na nic. Teprve poté, co byl odkaz explicitně inicializován - buď s odkazem na existující objekt, nebo s voláním Nový - odkazuje odkaz na skutečný objekt.

V sadě instrukcí JVM jsou všechny objekty instancovány a je k nim přistupováno se stejnou sadou operačních kódů, s výjimkou polí. V Javě jsou pole plnohodnotnými objekty a jako každý jiný objekt v programu Java se vytvářejí dynamicky. Odkazy na pole lze použít kdekoli jako odkaz na typ Objekt je požadována a jakákoli metoda Objekt lze vyvolat na poli. Přesto ve virtuálním stroji Java jsou pole zpracovávána se speciálními bajtovými kódy.

Stejně jako u jiných objektů nelze pole deklarovat jako lokální proměnné; mohou pouze odkazy na pole. Samotné objekty pole vždy obsahují buď pole primitivních typů, nebo pole odkazů na objekty. Pokud deklarujete pole objektů, získáte pole odkazů na objekty. Samotné objekty musí být explicitně vytvořeny pomocí Nový a přiřazeny k prvkům pole.

Opcodes pro objekty

Instance nových objektů se provádí pomocí

Nový

operační kód. Dva jednobajtové operandy následují za

Nový

operační kód. Tyto dva bajty jsou kombinovány a tvoří 16bitový index do fondu konstant. Konstantní prvek fondu v zadaném posunu poskytuje informace o třídě nového objektu. JVM vytvoří novou instanci objektu na haldě a vloží odkaz na nový objekt do zásobníku, jak je znázorněno níže.

Vytváření objektů
Operační kódOperand (s)Popis
Novýindexbyte1, indexbyte2vytvoří nový objekt na haldě, posune odkaz

Následující tabulka ukazuje operační kódy, které vkládají a získávají pole objektů. Tyto operační kódy, putfield a getfield, fungují pouze na polích, která jsou proměnnými instance. Ke statickým proměnným přistupují putstatické a getstatické, které jsou popsány dále. Každý z instrukcí putfield a getfield trvá dva jednobajtové operandy. Operandy jsou kombinovány a tvoří 16bitový index do fondu konstant. Položka konstantního fondu v tomto indexu obsahuje informace o typu, velikosti a posunutí pole. Odkaz na objekt je převzat ze zásobníku v pokynech putfield i getfield. Instrukce putfield převezme hodnotu proměnné instance ze zásobníku a instrukce getfield vloží hodnotu proměnné načtené instance do zásobníku.

Přístup k proměnným instance
Operační kódOperand (s)Popis
Putfieldindexbyte1, indexbyte2nastavit pole, označené indexem, objektu na hodnotu (obě převzaty ze zásobníku)
getfieldindexbyte1, indexbyte2tlačí pole (označené indexem) objektu (převzato ze zásobníku)

K proměnným třídy se přistupuje pomocí getstatic a putstatic opcodes, jak je uvedeno v tabulce níže. Jak getstatic, tak putstatic vezmou dva jednobajtové operandy, které JVM zkombinuje a vytvoří 16bitový nepodepsaný offset do fondu konstant. Položka konstantního fondu na tomto místě poskytuje informace o jednom statickém poli třídy. Protože ke statickému poli není přidružen žádný konkrétní objekt, neexistuje žádný odkaz na objekt používaný metodou getstatic ani putstatic. Putstatická instrukce přebírá hodnotu, která se má přiřadit ze zásobníku. Instrukce getstatic posune načtenou hodnotu do zásobníku.

Přístup k proměnným třídy
Operační kódOperand (s)Popis
putstatickýindexbyte1, indexbyte2nastavit pole, označené indexem, objektu na hodnotu (obě převzata ze zásobníku)
getstatickýindexbyte1, indexbyte2tlačí pole (označené indexem) objektu (převzato ze zásobníku)

Následující operační kódy zkontrolují, zda odkaz na objekt v horní části zásobníku odkazuje na instanci třídy nebo rozhraní indexovaného operandy následujícími po operačním kódu. Instrukce checkcast hodí CheckCastException pokud objekt není instancí zadané třídy nebo rozhraní. Jinak checkcast nedělá nic. Odkaz na objekt zůstává v zásobníku a provádění pokračuje v další instrukci. Tato instrukce zajišťuje, že vrhače jsou bezpečné za běhu a tvoří součást bezpečnostní přikrývky JVM.

Instrukce instanceof vyskočí na odkaz na objekt z horní části zásobníku a stiskne true nebo false. Pokud je objekt skutečně instancí zadané třídy nebo rozhraní, pak se do zásobníku vloží true, jinak se do zásobníku vloží false. Instrukce instanceof se používá k implementaci instanceof klíčové slovo Java, které umožňuje programátorům otestovat, zda je objekt instancí konkrétní třídy nebo rozhraní.

Kontrola typu
Operační kódOperand (s)Popis
checkcastindexbyte1, indexbyte2Vyvolá ClassCastException, pokud objectref na zásobníku nelze přetypovat do třídy v indexu
instanceofindexbyte1, indexbyte2Pushes true, pokud objectref na zásobníku je instancí třídy v indexu, jinak tlačí false

Opcodes pro pole

Instance nových polí se provádí pomocí oparodů newarray, anewarray a multianewarray. Operační kód newarray se používá k vytváření polí primitivních typů jiných než odkazy na objekty. Konkrétní primitivní typ je určen jediným jednobajtovým operandem následujícím po operačním kódu newarray. Instrukce newarray může vytvářet pole pro byte, short, char, int, long, float, double nebo boolean.

Instrukce anewarray vytvoří řadu odkazů na objekty. Dva jednobajtové operandy následují operační kód anewarray a jsou kombinovány tak, aby vytvořily 16bitový index do konstantního fondu. Popis třídy objektu, pro který má být pole vytvořeno, se nachází ve fondu konstant u zadaného indexu. Tato instrukce přiděluje prostor pro pole odkazů na objekty a inicializuje odkazy na null.

Instrukce multianewarray se používá k přidělení vícerozměrných polí - což jsou jednoduše pole polí - a lze je přidělit opakovaným použitím instrukcí anewarray a newarray. Multianewarray instrukce jednoduše komprimuje bajtové kódy potřebné k vytvoření vícerozměrných polí do jedné instrukce. Dva jednobajtové operandy následují víceřádkový operační kód a jsou kombinovány do 16bitového indexu do konstantního fondu. Popis třídy objektu, pro který má být pole vytvořeno, se nachází ve fondu konstant u zadaného indexu. Bezprostředně po dvou jednobajtových operandech, které tvoří index konstantního fondu, je jednobajtový operand, který určuje počet dimenzí v tomto vícerozměrném poli. Velikosti pro každou dimenzi se vyskočí ze zásobníku. Tato instrukce přiděluje prostor pro všechna pole, která jsou potřebná k implementaci vícerozměrných polí.

Vytváření nových polí
Operační kódOperand (s)Popis
newarraytypobjeví délku, přidělí nové pole primitivních typů typu označeného atype, posune objectref nového pole
znovuindexbyte1, indexbyte2objeví délku, přidělí nové pole objektů třídy označené indexbyte1 a indexbyte2, posune objectref nového pole
multianewarrayindexbyte1, indexbyte2, rozměryobjeví rozměry počet délek pole, přidělí nové vícerozměrné pole třídy označené indexbyte1 a indexbyte2, vloží objektref nového pole

Následující tabulka ukazuje instrukci, která vysune odkaz na pole z horní části zásobníku a posune délku tohoto pole.

Získání délky pole
Operační kódOperand (s)Popis
délka pole(žádný)objeví objectref pole, posune délku tohoto pole

Následující operační kódy načtou prvek z pole. Index pole a odkaz na pole jsou vyskakovány ze zásobníku a hodnota v zadaném indexu zadaného pole je vrácena zpět do zásobníku.

Načítání prvku pole
Operační kódOperand (s)Popis
Baload(žádný)objeví index a arrayref pole bajtů, posune arrayref [index]
náklad(žádný)objeví index a arrayref řady znaků, posune arrayref [index]
pytel(žádný)objeví index a arrayref řady šortek, posune arrayref [index]
iaload(žádný)objeví index a arrayref řady ints, posune arrayref [index]
zátěž(žádný)objeví index a arrayref řady longů, posune arrayref [index]
faload(žádný)objeví index a arrayref řady plováků, posune arrayref [index]
daload(žádný)objeví index a arrayref z pole zdvojnásobí, posune arrayref [index]
aaload(žádný)objeví index a arrayref z pole objectrefs, posune arrayref [index]

Následující tabulka ukazuje operační kódy, které ukládají hodnotu do prvku pole. Hodnota, index a odkaz na pole jsou vyskakovány z horní části zásobníku.

Ukládání do prvku pole
Operační kódOperand (s)Popis
bastore(žádný)objeví hodnotu, index a arrayref pole bajtů, přiřadí arrayref [index] = hodnota
Castore(žádný)objeví hodnotu, index a arrayref pole znaků, přiřadí arrayref [index] = hodnota
sastore(žádný)objeví hodnotu, index a arrayref řady šortek, přiřadí arrayref [index] = hodnota
iastore(žádný)objeví hodnotu, index a arrayref pole ints, přiřadí arrayref [index] = hodnota
vydržet(žádný)objeví hodnotu, index a arrayref pole longů, přiřadí arrayref [index] = hodnota
rychle(žádný)objeví hodnotu, index a arrayref pole plováků, přiřadí arrayref [index] = hodnota
dastore(žádný)objeví hodnotu, index a arrayref pole čtyřhry, přiřadí arrayref [index] = hodnota
aastore(žádný)objeví hodnotu, index a arrayref z pole objectrefs, přiřadí arrayref [index] = hodnota

Trojrozměrné pole: simulace virtuálního stroje Java

Níže uvedený applet ukazuje virtuální stroj Java provádějící sekvenci bajtových kódů. Sekvence bytecode v simulaci byla vygenerována uživatelem javac pro initAnArray () metoda třídy uvedené níže:

třída 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 [ i] [j] [k] = i + j + k; }}}}} 

Bajtové kódy generované javac pro initAnArray () jsou zobrazeny níže: