Obsah
1. Malé zopakování z minula – struktura položek uložených v constant poolu
2. Nástroj pro zobrazení obsahu všech položek uložených v constant poolu
3. Formát constant poolu pro zcela prázdnou třídu odvozenou od třídy Object
4. 32bitové číselné konstanty použité v programovém kódu
5. 8bitové, 16bitové a 64bitové číselné konstanty
6. Způsob určení datových typů atributů a návratových hodnot metod
7. Demonstrační příklad – výpis návratových typů metod
8. Demonstrační příklad – výpis typů argumentů metod
1. Malé zopakování z minula – struktura položek uložených v constant poolu
V dnešní části seriálu o programovacím jazyce Java i o vlastnostech virtuálního stroje tohoto jazyka se opět budeme zabývat popisem interní struktury souborů .class, tj. souborů obsahujících bajtkód jedné třídy, rozhraní nebo výčtu (enumeration). V předchozí části seriálu jsme si řekli, jak tyto soubory vlastně vznikají a jaká je jejich základní struktura. Taktéž jsme se zmínili o takzvaném constant poolu, jenž je nedílnou součástí bajtkódu. Právě této části bajtkódu se dnes budeme věnovat podrobněji. Připomeňme si, že v constant poolu se mohou nacházet položky několika typů. Typ každé položky je jednoznačně určen takzvaným tagem (jednobajtovou konstantou), za nímž následuje buď celočíselná či reálná konstanta, řetězec se zadanou délkou, popř. jeden či dva odkazy na další položku/položky umístěné v constant poolu.
Nejjednodušší strukturu mají položky, které přímo obsahují celočíselné či reálné konstanty. Jejich použití si podrobněji popíšeme ve čtvrté a taktéž v páté kapitole. Jedná se o položky s tagy 3, 4, 5 a 6:
Tag | Název tagu | Parametr 1 | Parametr 2 |
---|---|---|---|
3 | Integer | čtyřbajtová celočíselná konstanta | × |
4 | Float | čtyřbajtová FP konstanta | × |
5 | Long | osmibajtová celočíselná konstanta | × |
6 | Double | osmibajtová FP konstanta | × |
V každém constant poolu se taktéž nachází několik řetězců (majících různý význam), popř. i řetězcových konstant (literálů):
Tag | Název tagu | Parametr 1 | Parametr 2 |
---|---|---|---|
1 | Utf8 | délka řetězce v bajtech | sekvence bajtů (počet znaků je obecně menší než počet bajtů) |
8 | String | 2 bajty: odkaz na vlastní řetězec (záznam typu Utf8) | × |
Dalších pět typů záznamů již neobsahuje žádné konstanty (ani číselné, ani řetězcové), ale odkazy na další položky umístěné v constant poolu. O způsobu práce s odkazy se podrobněji zmíníme ve třetí kapitole. Jedná se o položky s tagy 7, 9, 10, 11 a 12:
Tag | Název tagu | Parametr 1 | Parametr 2 |
---|---|---|---|
7 | Class | 2 bajty: odkaz na jméno třídy (záznam typu Utf8) | × |
9 | Fieldref | 2 bajty: odkaz na jméno třídy (záznam typu Utf8) | 2 bajty: odkaz na záznam typu NameAndType |
10 | Methodref | 2 bajty: odkaz na jméno třídy (záznam typu Utf8) | 2 bajty: odkaz na záznam typu NameAndType |
11 | InterfaceMethodref | 2 bajty: odkaz na jméno třídy (záznam typu Utf8) | 2 bajty: odkaz na záznam typu NameAndType |
12 | NameAndType | 2 bajty: odkaz na záznam typu Utf8 | 2 bajty: odkaz na záznam typu Utf8 |
2. Nástroj pro zobrazení obsahu všech položek uložených v constant poolu
Javovský bajtkód i constant pool, jenž je jeho nedílnou součástí, je samozřejmě možné zkoumat mnoha různými způsoby. Pro vysokoúrovňovou manipulaci s bajtkódem je pravděpodobně nejlepší použít již minule zmíněné knihovny ASM a BCEL, k nimž se ještě v tomto seriálu vrátíme. Pro pouhý výpis obsahu constant poolu nicméně postačuje standardní utilita nazvaná javap, které je spuštěna s přepínačem -verbose. Avšak pro pochopení způsobu uložení informací v constant poolu je nejlepší si vytvořit vlastní program, který vhodným způsobem vypíše jeho obsah. Zdrojový kód tohoto programu (mimochodem, jedná se o výsledek jednoho letošního zimního večera :-) můžete získat na adrese http://i.iinfo.cz/files/root/145/decompiler-source.c, nicméně pro prohlížení jeho zdrojového kódu přímo v prohlížeči je lepší použít tento odkaz, pod nímž se skrývá zdrojový kód s obarvenou syntaxí.
Tento jednoduchý nástroj, který s velkou pravděpodobností obsahuje různé zákeřné chyby :-), je možné přeložit pomocí překladače odpovídajícího normě ANSI/ISO C, bude však pracovat korektně pouze na procesorech s pořadím bajtů little endian (na procesorech big endian se mohou špatně zpracovávat načtené číselné konstanty, ostatní části programu jsou však nezávislé na architektuře). Po překladu vznikne spustitelný soubor, který akceptuje jeden parametr – název souboru .class. Název se uvádí i s příponou, na rozdíl od utility javap (podle mého názoru je uvedení přípony souboru většinou praktičtější). Výsledek, tj. obsah constant poolu (plus několik dalších základních informací) je tisknut na standardní výstup. Princip práce tohoto programu je velmi jednoduchý – nejdříve je z bajtkódu získán obsah všech položek uložených v constant poolu, který je uložen do dynamicky alokovaného pole. Posléze je obsah tohoto pole vypsán, přičemž dochází k dereferencování některých informací (například odkaz na položky typu string je přímo nahrazen referencovaným řetězcem).
Důvod, proč jsou položky nejdříve z constant poolu načteny do dynamicky alokovaného pole, spočívá v tom, že reference mohou odkazovat na libovolné další položky. Typicky se totiž používají i dopředné reference, což znamená, že není možné, aby se obsah constant poolu ihned při načítání převáděl na standardní výstup. O načítání položek z bajtkódu se stará funkce process_constant_pool() společně s funkcí read_constant_pool_entry(). Výsledkem je dynamické pole s prvky typu PoolEntry (jde o strukturu obsahující jak tagy jednotlivých prvků, tak i vlastní data – číselné konstanty, řetězce či odkazy/reference). Všechny prvky uložené v tomto poli jsou posléze postupně vytištěny funkcí print_constant_pool_entry(), z níž se volají další pomocné funkce print_(typ_prvku). Podrobnější informace o tom, proč je program vytvořen právě takovým způsobem, budou vysvětleny v dalších kapitolách (asi nejzáhadnější je návratová hodnota funkce read_constant_pool_entry() a způsob jejího použití při naplňování pole položkami získanými z constant poolu).
3. Formát constant poolu pro zcela prázdnou třídu odvozenou od třídy Object
Pojďme se nyní podívat na to, jak vypadá obsah constant poolu v reálných .class souborech, tj. v souborech vzniklých zkompilováním jediné třídy (či rozhraní, popř. výčtu). Začněme prakticky nejjednodušší třídou – prázdnou třídou odvozenou implicitně od třídy Object (jak jistě víte, každá třída, kterou programátor deklaruje, musí mít předka a pokud není předek explicitně uveden za klíčovým slovem extends, použije se jako implicitní předek právě třída Object). Na zdrojovém textu s definicí této třídy není nic záhadného ani složitého:
public class EmptyClass { }
Nyní již můžeme snadno zjistit, jaké informace nám zobrazí výše popsaný nástroj. Poznamenejme, že překlad třídy EmptyClass byl proveden s přepínačem -g:none, aby se odstranily pro nás nepotřebné dodatkové informace (použití tohoto přepínače není nutné, ovšem nám zjednoduší analýzu obsahu constant poolu):
Magicka konstanta: cafebabe Cislo verze: 50 Cislo podverze: 0 Velikost const. poolu: 9 prvku 1 10 MethodRef 3 7 java/lang/Object.<init>()V 2 7 Class 8 EmptyClass 3 7 Class 9 java/lang/Object 4 1 String "<init>" 5 1 String "()V" 6 1 String "Code" 7 12 Name and type 4 5 <init> ()V 8 1 String "EmptyClass" 9 1 String "java/lang/Object"
Zajímavé jistě je, že i pro tak jednoduchou a zdánlivě primitivní třídu obsahuje constant pool celých devět položek. Díky tomu, že se v pátém a šestém sloupci zobrazují reference na další položky poolu, lze snadno analyzovat druhou a třetí položku, která obsahuje jména tříd (přesněji řečeno odkazy na řetězce se jmény tříd) – konkrétně se jedná o plné názvy (názvy i s příslušným balíkem) naší prázdné třídy a jejího předka (Object):
1 10 MethodRef 3 7 java/lang/Object.<init>()V 2 7 Class 8---------. EmptyClass 3 7 Class 9------. | java/lang/Object 4 1 String | | "<init>" 5 1 String | | "()V" 6 1 String | | "Code" 7 12 Name and type 4 5 | | <init> ()V 8 1 String | `--> "EmptyClass" 9 1 String `-----> "java/lang/Object"
Dále je zajímavá sedmá položka, která obsahuje dva odkazy – odkaz na jméno (metody, atributu, rozhraní) a odkaz na typ. Oba odkazy musí směřovat na prvky typu řetězec:
1 10 MethodRef 3 7 java/lang/Object.<init>()V 2 7 Class 8 EmptyClass 3 7 Class 9 java/lang/Object 4 1 String .----------> "<init>" 5 1 String | .-----> "()V" 6 1 String | | "Code" 7 12 Name and type 4-' 5--' <init> ()V 8 1 String "EmptyClass" 9 1 String "java/lang/Object"
Nejsložitější je hned první položka. Ta obsahuje informaci o metodě. Tato informace je rozdělena na dvě reference. První reference ukazuje na jméno třídy (řetězec se jménem třídy) a druhá reference ukazuje na prvek typu Name and type, který taktéž obsahuje dvě reference – viz též předchozí odstavec:
.----------------------------------. | | 1 10 MethodRef 3-. 7----' java/lang/Object.<init>()V | 2 7 Class 8 | EmptyClass | 3 7 Class 9 `----------> java/lang/Object | 4 1 String .----------> "<init>" | 5 1 String | .-----> "()V" | 6 1 String | | "Code" | 7 12 Name and type 4-' 5--' <init> ()V <----------------' 8 1 String "EmptyClass" 9 1 String "java/lang/Object"
O jakou metodu se však v tomto případě jedná, když je naše třída (zdánlivě) prázdná? Jak jste již možná uhodli, tak naše třída ve skutečnosti při své inicializaci bude volat konstruktor předka a konstruktory se v bajtkódu pojmenovávají <init>. Jedná se o jméno, které nelze ve zdrojovém kódu použít, proto nikdy nedojde ke kolizím s uživatelskou metodou či atributem.
4. 32bitové číselné konstanty použité v programovém kódu
Již z předchozího příkladu je pravděpodobně patrné, že constant pool může obsahovat poměrně velké množství důležitých informací, a to i pro relativně jednoduché třídy. Má to svůj význam, protože v instrukční sadě se mnohé instrukce odkazují právě na prvky umístěné v constant poolu, což na jednu stranu vede k větší „informační hustotě“ instrukční sady, tak i k tomu, že se v sadě nenachází žádné přímé volání (zejména cizích) metod, protože v JVM lze až za běhu aplikace určit (pomocí classloaderů), které třídy se mají nahrát a dynamicky slinkovat. Jak již víme z předchozí části tohoto seriálu (i z úvodní kapitoly), jsou v constant poolu umístěny i celočíselné konstanty, popř. konstanty čísel reprezentovaných v systému plovoucí řádové čárky (floating point). Nejjednodušší je situace v případě, kdy jsou ve zdrojovém kódu třídy použity numerické konstanty typu int a float, tak, jak je tomu u naší další demonstrační třídy pojmenované ConstantsTest1:
public class ConstantsTest1 { public static final int integerField = 400000; public static final float floatField = 123.456f; }
Obsah constant poolu je již poněkud složitější, prozatím však budeme některé položky ignorovat (tyto nové položky jsou určeny pro uložení jména a typu obou deklarovaných atributů, s devíti dalšími položkami jsme se již setkali v předchozí kapitole):
Magicka konstanta: cafebabe Cislo verze: 50 Cislo podverze: 0 Velikost const. poolu: 16 prvku 1 10 MethodRef 3 14 java/lang/Object.<init>()V 2 7 Class 15 ConstantsTest1 3 7 Class 16 java/lang/Object 4 1 String "integerField" 5 1 String "I" 6 1 String "ConstantValue" 7 3 Integer 400000 8 1 String "floatField" 9 1 String "F" 10 4 Float 123.456001 11 1 String "<init>" 12 1 String "()V" 13 1 String "Code" 14 12 Name and type 11 12 <init> ()V 15 1 String "ConstantsTest1" 16 1 String "java/lang/Object"
V této chvíli nás zajímá pouze dvojice položek s konstantami – jedná se o položku číslo sedm a deset:
7 3 Integer 400000 10 4 Float 123.456001
Jak můžete sami vidět, jedná se vlastně o ten nejjednodušší typ záznamu uloženého v constant poolu. Fyzická podoba těchto konstant je zřejmá: za tagem (s číslem 3 nebo 4) jsou uloženy čtyři bajty představující buď 32bitové celé číslo se znaménkem (bezznaménkové celočíselné konstanty ani proměnné Java nezná), nebo taktéž 32bitové reálné číslo jednoduché přesností. Pořadí uložení těchto čtyř bajtů je vždy big endian.
5. 8bitové, 16bitové a 64bitové číselné konstanty
Zatímco u číselných konstant typu int a float byla situace velmi jednoduchá, u číselných konstant dalších typů již můžeme narazit na několik zajímavostí. První zajímavostí je, že celočíselné typy s menším rozsahem než int jsou v constant poolu uloženy poněkud neúsporně jako int, což znamená, že i osmibitová konstanta typu byte je uložena jako pětibajtová položka (prvním bajtem je tag, další čtyři bajty představují 32bitové celé číslo se znaménkem). Podobné je to i u konstant typu short a char. Pokud by však byla takto malá celočíselná konstanta použita pouze někde ve výrazu a nikoli pro deklaraci a inicializaci třídního atributu, nemusí být v constant poolu uložena vůbec, protože pro práci s „malými“ konstantami slouží instrukce typu sipush či bipush (viz též další díl tohoto seriálu, v němž se konečně dostaneme i přímo k instrukční sadě používané virtuálním strojem Javy).
Zbývají nám tedy ještě celá 64bitová čísla typu long a čísla s plovoucí řádovou čárkou s dvojitou přesností double. Ty jsou v constant poolu taktéž uloženy přímo jako konstanty, ovšem ihned za těmito položkami je nutné při vytváření obrazu constant poolu v operační paměti další položku bajtkódu vynechat. Pokud by se na tuto (vpravdě nelogickou!) operaci zapomnělo, byly by všechny reference/odkazy na položky v constant poolu špatně očíslované (navíc i celkový počet položek constant poolu, který je uložen v bajtkódu, s těmito výplněmi počítá). To je taktéž důvod pro to, aby funkce read_constant_pool_entry() z naší jednoduché utility vracela číslo určující počet položek, o kolik je nutné se ve vytvářeném obrazu constant poolu posunout. Podívejme se konečně na demonstrační příklad:
public class ConstantsTest2 { public static final long longField = 400000; public static final double doubleField = 123.456f; public static final byte byteField = 100; public static final short shortField = 10000; public static final char charField = 20000; }
Obsah constant poolu vypadá následovně (ve skutečnosti se výplňové položky v bajtkódu neukládají, v paměti je však musíme vytvářet):
Magicka konstanta: cafebabe Cislo verze: 50 Cislo podverze: 0 Velikost const. poolu: 27 prvku 1 10 MethodRef 3 25 java/lang/Object.<init>()V 2 7 Class 26 ConstantsTest2 3 7 Class 27 java/lang/Object 4 1 String "longField" 5 1 String "J" 6 1 String "ConstantValue" 7 5 Long 400000 8 0 Neznamy tag 9 1 String "doubleField" 10 1 String "D" 11 6 Double 123.456001 12 0 Neznamy tag 13 1 String "byteField" 14 1 String "B" 15 3 Integer 100 16 1 String "shortField" 17 1 String "S" 18 3 Integer 10000 19 1 String "charField" 20 1 String "C" 21 3 Integer 20000 22 1 String "<init>" 23 1 String "()V" 24 1 String "Code" 25 12 Name and type 22 23 <init> ()V 26 1 String "ConstantsTest2" 27 1 String "java/lang/Object"
Pro nás jsou v tuto chvíli zajímavé pouze následující položky, k nimž jsem doplnil komentáře:
7 5 Long 400000 8 0 Neznamy tag *** výplň ihned za "long" 11 6 Double 123.456001 12 0 Neznamy tag *** výplň ihned za "double" 15 3 Integer 100 18 3 Integer 10000 *** pro konstantu typu short 21 3 Integer 20000 *** pro konstantu typu char
6. Způsob určení datových typů atributů a návratových hodnot metod
Další důležité informace, které jsou v constant poolu uložené, se týkají typů atributů tříd, popř. návratových typů metod i datových typů argumentů metod. Informace o všech těchto datových typech jsou určeny řetězcem, který se v případě primitivních datových typů skládá z jediného znaku (velkého písmena „B“, „C“, „D“, „F“, „I“, „J“, „S“ a „Z“) a v případě referenčních typů z plného jména třídy, před nějž je vložen znak „L“ a jméno třídy je ukončeno středníkem. Speciálním případem jsou pole (která jsou v Javě taktéž považována za referenční typy). V případě polí je v řetězci zakódován jak počet dimenzí, tak i datové typy prvků, ze kterých se pole skládá (může se samozřejmě jednat jak o primitivní datové typy, tak i o referenční typy). Podívejme se nejdříve na znaky použité v řetězci, pomocí něhož se specifikuje datový typ atributů, návratový typ metod i typy jednotlivých argumentů metod:
# | Znak/řetězec | Datový typ | Význam |
---|---|---|---|
1 | B | byte | osmibitové celé číslo se znaménkem |
2 | C | char | znak reprezentovaný v Unikódu |
3 | D | double | číslo s plovoucí řádovou čárkou s dvojitou přesností |
4 | F | float | číslo s plovoucí řádovou čárkou s jednoduchou přesností |
5 | I | int | 32bitové celé číslo se znaménkem |
6 | J | long | 64bitové celé číslo se znaménkem |
7 | Ljméno_třídy; | reference | instance třídy zadaného jména |
8 | S | short | 16bitové celé číslo se znaménkem |
9 | Z | boolean | pravdivostní hodnota true/false |
10 | [ | reference | jednodimenzionální pole |
Podívejme se na jednoduchý příklad. Jedná se o třídu obsahující tři atributy – skalární celočíselnou proměnnou nazvanou promenna, jednorozměrné pole s prvky typu int nazvané pole a konečně dvourozměné pole s prvky typu int nazvané matice:
public class Test { public int promenna; public int[] pole; public int[][] matice; }
Po překladu této třídy do bajtkódu najdeme v jeho constant poolu mj. i následujících šest položek, které popisují všechny tři atributy:
4 1 String "promenna" 5 1 String "I" 6 1 String "pole" 7 1 String "[I" 8 1 String "matice" 9 1 String "[[I"
Jak jsou tyto položky navzájem propojeny si řekneme příště, nás však v tuto chvíli zajímají především signatury atributů. V souladu s tabulkou uvedenou na začátku této kapitoly je skalární celočíselný atribut popsán řetězcem „I“. Jednorozměrné pole celočíselných prvků je popsáno řetězcem „[I“ (znak „[“ signalizuje, že se jedná o pole. Popis dvojrozměrného pole celočíselných prvků je určen řetězcem „[[I“, což lze číst jako pole polí celých čísel.
Co se stane, pokud použijeme namísto celočíselného datového typu typ referenční? Proveďme v kódu nepatrnou změnu:
public class Test { public String promenna; public String[] pole; public String[][] matice; }
V constant poolu v tomto případě dojde ke změnám, které opět odpovídají informacím uvedeným v tabulce uvedené na začátku této kapitoly – bude zde uloženo plně kvalifikované jméno třídy s prefixem „L“ a ukončujícím znakem středník:
4 1 String "promenna" 5 1 String "Ljava/lang/String;" 6 1 String "pole" 7 1 String "[Ljava/lang/String;" 8 1 String "matice" 9 1 String "[[Ljava/lang/String;"
7. Demonstrační příklad – výpis návratových typů metod
V dalším demonstračním příkladu si řekneme, jakým způsobem lze z constant poolu zjistit návratové typu metod. Ve třetí kapitole jsme si ukázali, jak je možné získat celou informaci o metodě, která je někde uvnitř třídy buď implicitně nebo explicitně volána. Připomeňme si, že informace o volané metodě je uložena v položce typu MethodRef, která obsahuje dva odkazy. První odkaz vede na položku typu Class s plným jménem třídy (přesněji řečeno s odkazem na řetězec nesoucí jméno třídy) a druhý odkaz vede na položku typu Name and type obsahující název metody, typy jejich parametrů (ne jejich jména) a taktéž typ návratové hodnoty. V následujícím testovacím příkladu je vytvořeno sedm bezparametrických metod, které vracejí hodnoty různých datových typů. Metody je v programovém kódu nutné zavolat, jinak by se v constant poolu neobjevily všechny podstatné informace. Volání je zařízeno v metodě main:
public class Methods2 { public void methodReturningVoid() {} public int methodReturningInteger() {return 1;} public byte methodReturningByte() {return 1;} public float methodReturningFloat() {return 1.0f;} public double methodReturningDouble() {return 1.0;} public int[] methodReturningArray() {return null;} public Object methodReturningObject() {return null;} public static void main(String[] args) { Methods2 m = new Methods2(); m.methodReturningVoid(); m.methodReturningInteger(); m.methodReturningByte(); m.methodReturningFloat(); m.methodReturningDouble(); m.methodReturningArray(); m.methodReturningObject(); } }
Po překladu této třídy a použití pomocného nástroje z druhé kapitoly můžeme z constant poolu získat (samozřejmě kromě dalších informací) i sedm položek typu MethodRef:
4 10 MethodRef 2 32 Methods2.methodReturningVoid()V 5 10 MethodRef 2 33 Methods2.methodReturningInteger()I 6 10 MethodRef 2 34 Methods2.methodReturningByte()B 7 10 MethodRef 2 35 Methods2.methodReturningFloat()F 8 10 MethodRef 2 36 Methods2.methodReturningDouble()D 9 10 MethodRef 2 37 Methods2.methodReturningArray()[I 10 10 MethodRef 2 38 Methods2.methodReturningObject()Ljava/lang/Object;
Z tohoto výpisu je patrné, že virtuální stroj Javy má k dispozici jak plný název třídy, tak i název metody společně s jejím návratovým typem. Ten je v našem případě součástí referencovaných položek typu Name and type, z nichž vede odkaz na řetězec s typem parametrů (zde pouze prázdné závorky) i návratovým typem umístěným za závorkami. Způsob „pojmenování“ návratového datového typu je shodný se způsobem popsaným v předchozí kapitole s tím rozšířením, že metody, které nevrací žádná data, mají nastaven návratový typ „V“ jako void (ten samozřejmě nebyl u třídních atributů použit).
Pro úplnost ještě následuje výpis obsahu celého constant poolu, všechny důležité položky však byly vysvětleny výše:
Magicka konstanta: cafebabe Cislo verze: 50 Cislo podverze: 0 Velikost const. poolu: 39 prvku 1 10 MethodRef 11 30 java/lang/Object.<init>()V 2 7 Class 31 Methods2 3 10 MethodRef 2 30 Methods2.<init>()V 4 10 MethodRef 2 32 Methods2.methodReturningVoid()V 5 10 MethodRef 2 33 Methods2.methodReturningInteger()I 6 10 MethodRef 2 34 Methods2.methodReturningByte()B 7 10 MethodRef 2 35 Methods2.methodReturningFloat()F 8 10 MethodRef 2 36 Methods2.methodReturningDouble()D 9 10 MethodRef 2 37 Methods2.methodReturningArray()[I 10 10 MethodRef 2 38 Methods2.methodReturningObject()Ljava/lang/Object; 11 7 Class 39 java/lang/Object 12 1 String "<init>" 13 1 String "()V" 14 1 String "Code" 15 1 String "methodReturningVoid" 16 1 String "methodReturningInteger" 17 1 String "()I" 18 1 String "methodReturningByte" 19 1 String "()B" 20 1 String "methodReturningFloat" 21 1 String "()F" 22 1 String "methodReturningDouble" 23 1 String "()D" 24 1 String "methodReturningArray" 25 1 String "()[I" 26 1 String "methodReturningObject" 27 1 String "()Ljava/lang/Object;" 28 1 String "main" 29 1 String "([Ljava/lang/String;)V" 30 12 Name and type 12 13 <init> ()V 31 1 String "Methods2" 32 12 Name and type 15 13 methodReturningVoid ()V 33 12 Name and type 16 17 methodReturningInteger ()I 34 12 Name and type 18 19 methodReturningByte ()B 35 12 Name and type 20 21 methodReturningFloat ()F 36 12 Name and type 22 23 methodReturningDouble ()D 37 12 Name and type 24 25 methodReturningArray ()[I 38 12 Name and type 26 27 methodReturningObject ()Ljava/lang/Object; 39 1 String "java/lang/Object"
8. Demonstrační příklad – výpis typu argumentů metod
Zbývá nám již popsat poslední maličkost – způsob určení datových typů argumentů metod. Jedná se o velmi důležitý údaj, protože právě na základě těchto informací je určeno, která (potenciálně přetížená) metoda se ve skutečnosti zavolá. Toto rozhodnutí provádí překladač, který volání konkrétní metody přeloží do instrukce invokevirtual. Podívejme se nyní na zdrojový kód demonstračního příkladu, v němž je jedenáct metod s různým počtem a typy argumentů. Metody jsou opět volány z metody main, aby se v constant poolu objevily všechny důležité informace:
public class Methods3 { public void noParams() {} public void oneInteger(int x) {} public void twoIntegers(int x, int y) {} public void twoFloats(float x, float y) {} public void intArray(int[] a) {} public void int2DArray(int[][] a) {} public void int3DArray(int[][][] a) {} public void twoArrays(int[] a, float[] b) {} public void object(Object o) {} public void objectArray(Object[] o) {} public void object2DArray(Object[][] o) {} public static void main(String[] args) { Methods3 m = new Methods3(); m.noParams(); m.oneInteger(1); m.twoIntegers(1,2); m.twoFloats(1.0f, 2.0f); m.intArray(null); m.int2DArray(null); m.int3DArray(null); m.twoArrays(null, null); m.object(null); m.objectArray(null); m.object2DArray(null); } }
V bajtkódu můžeme jednoduše nalézt informace o všech jedenácti metodách:
4 10 MethodRef 2 47 Methods3.noParams()V 5 10 MethodRef 2 48 Methods3.oneInteger(I)V 6 10 MethodRef 2 49 Methods3.twoIntegers(II)V 7 10 MethodRef 2 50 Methods3.twoFloats(FF)V 8 10 MethodRef 2 51 Methods3.intArray([I)V 10 10 MethodRef 2 53 Methods3.int2DArray([[I)V 12 10 MethodRef 2 55 Methods3.int3DArray([[[I)V 13 10 MethodRef 2 56 Methods3.twoArrays([I[F)V 14 10 MethodRef 2 57 Methods3.object(Ljava/lang/Object;)V 15 10 MethodRef 2 58 Methods3.objectArray([Ljava/lang/Object;)V 17 10 MethodRef 2 60 Methods3.object2DArray([[Ljava/lang/Object;)V
Pro tuto chvíli nás nemusí zajímat návratové typy, protože ty jsou ve všech případech zakódovány znakem „V“ (void). Důležitější jsou typy argumentů. Jak je z výpisu constant poolu patrné, jsou typy argumentů umístěny do závorek, čímž je automaticky určen i jejich počet. Metoda bez parametrů obsahuje pouze prázdné závorky (stejně jako v samotné Javě), metoda s jedním parametrem obsahuje řetězec shodný s typem atributu (nesmí se zde objevit „V“) a pokud má metoda více parametrů, jsou jejich typy jednoduše vypsány za sebou bez použití oddělovače – ten není zapotřebí. Příkladem může být metoda:
public void twoArrays(int[] a, float[] b) {}
jejíž reference vypadá v bajtkódu následovně:
13 10 MethodRef 2 56 Methods3.twoArrays([I[F)V
Řetězec v závorkách můžeme přečíst jako „pole prvků typu int následované polem prvků typu float“.
Pro úplnost bude opět vypsán obsah celého constant poolu, z něhož je patrné, že právě constant pool v naprosté většině případů tvoří největší objem bajtkódu:
Magicka konstanta: cafebabe Cislo verze: 50 Cislo podverze: 0 Velikost const. poolu: 61 prvku 1 10 MethodRef 18 45 java/lang/Object.<init>()V 2 7 Class 46 Methods3 3 10 MethodRef 2 45 Methods3.<init>()V 4 10 MethodRef 2 47 Methods3.noParams()V 5 10 MethodRef 2 48 Methods3.oneInteger(I)V 6 10 MethodRef 2 49 Methods3.twoIntegers(II)V 7 10 MethodRef 2 50 Methods3.twoFloats(FF)V 8 10 MethodRef 2 51 Methods3.intArray([I)V 9 7 Class 52 [[I 10 10 MethodRef 2 53 Methods3.int2DArray([[I)V 11 7 Class 54 [[[I 12 10 MethodRef 2 55 Methods3.int3DArray([[[I)V 13 10 MethodRef 2 56 Methods3.twoArrays([I[F)V 14 10 MethodRef 2 57 Methods3.object(Ljava/lang/Object;)V 15 10 MethodRef 2 58 Methods3.objectArray([Ljava/lang/Object;)V 16 7 Class 59 [[Ljava/lang/Object; 17 10 MethodRef 2 60 Methods3.object2DArray([[Ljava/lang/Object;)V 18 7 Class 61 java/lang/Object 19 1 String "<init>" 20 1 String "()V" 21 1 String "Code" 22 1 String "noParams" 23 1 String "oneInteger" 24 1 String "(I)V" 25 1 String "twoIntegers" 26 1 String "(II)V" 27 1 String "twoFloats" 28 1 String "(FF)V" 29 1 String "intArray" 30 1 String "([I)V" 31 1 String "int2DArray" 32 1 String "([[I)V" 33 1 String "int3DArray" 34 1 String "([[[I)V" 35 1 String "twoArrays" 36 1 String "([I[F)V" 37 1 String "object" 38 1 String "(Ljava/lang/Object;)V" 39 1 String "objectArray" 40 1 String "([Ljava/lang/Object;)V" 41 1 String "object2DArray" 42 1 String "([[Ljava/lang/Object;)V" 43 1 String "main" 44 1 String "([Ljava/lang/String;)V" 45 12 Name and type 19 20 <init> ()V 46 1 String "Methods3" 47 12 Name and type 22 20 noParams ()V 48 12 Name and type 23 24 oneInteger (I)V 49 12 Name and type 25 26 twoIntegers (II)V 50 12 Name and type 27 28 twoFloats (FF)V 51 12 Name and type 29 30 intArray ([I)V 52 1 String "[[I" 53 12 Name and type 31 32 int2DArray ([[I)V 54 1 String "[[[I" 55 12 Name and type 33 34 int3DArray ([[[I)V 56 12 Name and type 35 36 twoArrays ([I[F)V 57 12 Name and type 37 38 object (Ljava/lang/Object;)V 58 12 Name and type 39 40 objectArray ([Ljava/lang/Object;)V 59 1 String "[[Ljava/lang/Object;" 60 12 Name and type 41 42 object2DArray ([[Ljava/lang/Object;)V 61 1 String "java/lang/Object"
9. Odkazy na Internetu
- The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/