Obsah
1. Novinky v JDK 7 aneb mírný pokrok v mezích zákona
3. Projekt Coin – rozšíření syntaxe a sémantiky Javy
4. Nové možnosti při deklaraci numerických konstant
5. Příkaz switch v JDK 1.0 až JDK 5
6. Rychlý pohled „pod kapotu“ JDK
7. Použití řetězců v příkazu switch
8. Struktura generovaného bajtkódu při použití řetězců v příkazu switch
1. Novinky v JDK 7 aneb mírný pokrok v mezích zákona
Specifikace programovacího jazyka Java i jeho virtuálního stroje se postupně vyvíjí společně s tím, jak se rozšiřují jak možnosti počítačů a dalších programovacích jazyků, tak i způsoby nasazení Javy v praxi. V roce 1996 firma Sun Microsystems zveřejnila první verzi JDK 1.0 a ve verzích následujících docházelo k postupnému rozšiřování a vylepšování syntaxe a sémantiky tohoto programovacího jazyka, což se v některých případech projevilo i na nutnosti změny generovaného bajtkódu. Z vlastností jazyka, které byly postupně do Javy přidány, můžeme jmenovat například vnitřní třídy a reflexi (JDK 1.1), kolekce (J2SE 1.2), klíčové slovo assert (J2SE 1.4), generické typy, anotace, autoboxing, výčtový typ, smyčku typu for-each a variabilní počet parametrů metod (J2SE 5.0). Zatímco v následující verzi Java SE 6 se mnoho změn neobjevilo, jinak je tomu u specifikace Java SE 7 implementované v JDK 7 a OpenJDK7. Při rozhodování o tom, jaké nové vlastnosti by se měly v JDK 7 projevit, byly do úvahy brány mj. i kritéria popsaná v následujícím textu.
Hlavní prioritou při posuzování vhodnosti či nevhodnosti jednotlivých změn byla jasnost a srozumitelnost zdrojových kódů vytvářených v Javě. To je na první pohled rozumné kritérium, ovšem některé užitečné, ale na první pohled ne zcela jasné nebo jen pro javisty neobvyklé konstrukce, nakonec nebyly do Javy zařazeny. Například i přes existenci autoboxingu (JDK 5) stále nelze zapsat 1.toString(), ale jen ((Integer)1).toString(), new Integer(1).toString() nebo lépe Integer.valueOf(1).toString(). Druhým kritériem braným v úvahu bylo, že čtení přehledného kódu je důležitější než jeho snadný a úsporný zápis, což mj. znamená, že Java je poměrně „ukecaný“ jazyk: například settery a gettery je – alespoň prozatím – nutné psát explicitně namísto použití dekorátorů či anotací; podmínku, zda je nějaká reference rovna null, je nutné taktéž psát explicitně atd. Jiné programovací jazyky mají i odlišné priority a jsou směrovány k jiným typům aplikací. Dobrým příkladem může být Perl s mnoha „magickými“ proměnnými a úsporným zápisem.
2. Plán „A“ a plán „B“
Původní návrhy pro nové vlastnosti JDK 7 byly vytvořeny ještě ve firmě Sun Microsystems. Mělo se jednat o doslova revoluční verzi JDK, v níž se měly do programovacího jazyka Java dostat konstrukce známé spíše z funkcionálních programovacích jazyků (například lambda výrazy, ovšem typované!); z JVM se měla stát universální platforma pro mnoho dalších, většinou dynamicky typovaných programovacích jazyků atd. Ovšem celý vývoj JDK 7 se dostal do časového skluzu, což je ostatně už u mnoha velkých projektů spíše pravidlem než výjimkou ;-). Navíc mezitím firma Sun otevřela cca 95% svého JDK pod názvem OpenJDK a o nějaký čas později došlo k převzetí firmy Sun Microsystems firmou Oracle, takže se poněkud změnily priority vývoje (nejenom) JDK 7. Výsledkem všech těchto skutečností bylo sestavení dvou plánů: plánu „A“ a plánu „B“, s tím, že se po zralé úvaze rozhodne, který z plánů bude uskutečněn.
V plánu „A“ se počítalo s tím, že se vydá „revoluční“ JDK 7 s velkým množstvím změn, ovšem až v roce 2012, což je velký časový skluz. Přednosti tohoto plánu spočívají v tom, že se počet dostupných verzí podporovaných JDK nebude příliš zvyšovat, ovšem velkou nevýhodou je již zmíněný časový skluz. Naproti tomu se v plánu „B“ vývoj JDK 7 rozdělil na JDK 7, které má být dokončené již v polovině příštího roku a na JDK 8, které by snad mohlo být k dispozici na konci roku 2012. Předností tohoto plánu je to, že vývojáři budou moci již příští rok využít některé z nových (již implementovaných a otestovaných) vlastností JDK 7, ovšem nevýhoda spočívá ve větší fragmentaci JDK a jejich podpory: zatímco dnes se stále ještě poměrně často používá verze 1.4.2 a samozřejmě též 5.0 a 6.0, bylo by to v roce 2012 hned pět různých verzí JDK (podpora pro 1.4.2 a 5.0 sice teoreticky vyprší, ale mnoho firem nemůže z různých důvodů na novou verzi ihned přejít). Dá se tedy počítat s tím, že JDK 7 nebude přijato všemi vývojáři, protože si mnozí raději počkají až na JDK 8.
3. Projekt Coin – rozšíření syntaxe a sémantiky Javy
Na letošní konferenci JavaOne bylo prezentováno rozhodnutí o dalším vývoji JDK 7 – nakonec zvítězil plán „B“. Nové vlastnosti JDK tedy byly rozděleny do dvou množin, z nichž první bude (v podstatě již je) implementována v JDK 7 a druhá až v JDK 8 o dva roky později. V novinkách, které budou v JDK 7, je zahrnuta například podpora pro dynamicky typované jazyky běžící v JVM (nová instrukce v bajtkódu), část projektu Coin (změna syntaxe a sémantiky jazyka), rozšíření NIO.2, podpora XRender Graphics Pipeline operacemi implementovanými v Java 2D (s čímž souvisí i nárůst výkonu některých grafických operací, což si řekneme příště), projekt Nimbus pro Swingové aplikace (taktéž bude popsáno příště), zahrnutí nových verzí TLS 1.2 a JDBC 4.1, podpora pro Unicode 6.0 atd. Pro běžné programátory budou asi nejviditelnější změny, které jsou implementovány v rámci výše zmíněného projektu Coin.
Jedná se o projekt, který do programovacího jazyka Java vnáší malé, ale o to příjemnější změny v syntaxi a sémantice. V některých případech se jedná o pouhý „syntaktický cukr“, ale na další vlastnosti čekali programátoři mnohdy i několik let. Zajímavé je, že i přes rozšíření syntaxe jazyka Java nebylo nutné v rámci projektu Coin přidat do tohoto programovacího jazyka žádné nové klíčové slovo a taktéž se nemusel rozšiřovat bajtkód o další instrukce. Mezi nové prvky Javy, které projekt Coin přináší již do JDK 7 (v JDK 8 je těchto prvků mnohem více), patří především:
- Deklarace celočíselných binárních konstant
- Použití podtržítka pro lepší čitelnost numerických konstant
- Příkaz switch a řetězce
- „Diamant“ – zjednodušený zápis deklarací při použití generik
- Automatic Resource Management (ARM) – automatické volání metody close v rámci rozšířeného bloku try
- Vylepšené zpracování výjimek
První tři rozšíření budou popsána v dalších kapitolách, zbylé tři rozšíření až v navazujícím článku.
4. Nové možnosti při deklaraci numerických konstant
Mezi jedno z nejjednodušších rozšíření, které již byly v rámci projektu Coin implementovány, patří podpora pro zápis binárních numerických konstant, tj. čísel ve dvojkové soustavě. Java totiž převzala z programovacího jazyka C a C++ způsob zápisu konstant v osmičkové a šestnáctkové soustavě, ale doposud neexistovala možnost použití soustavy dvojkové (to se hodí například při nízkoúrovňové práci s bitovými příznaky a nově také například při zápisu masek v IPv6). Syntaxe je velmi jednoduchá – čísla v osmičkové soustavě začínají na 0, v soustavě šestnáctkové na 0× a v soustavě binární, jak již asi čtenář uhodl, na 0b. Nově je taktéž možné, aby se při zápisu celých i reálných čísel, bez ohledu na použitý základ číselné soustavy, mohly jednotlivé řády oddělovat podtržítkem. To, kde a zda vůbec bude podtržítko použito, záleží pouze na vývojáři (typicky se asi budou oddělovat miliony a tisíce). Obě popsaná rozšíření Javy, které se mimochodem nijak neprojeví na generovaném bajtkódu, si můžete s (Open)JDK7 snadno vyzkoušet:
public class NumericConstants { public static void main(String[] args) { int decimal1 = 1234567890; int decimal2 = 1_234_567_890; // oddělení tisíců, milionů a miliard long decimal3 = -99_88_77_66_55_44_33_22_11L; // nová možnost - binární čísla int binary1 = 0b01000010; long binary2 = 0b0000_0001_0010_0011_0100_0101; // poněkud neobvyklé ale možné - záporné binární hodnoty long binary3 = -0b0000_0001_0010_0011_0100_0101; int octal1 = 01234567; int octal2 = 012_34_56_77; long octal3 = 0666_555_444_333_222_111L; int hexadecimal1 = 0x12345678; int hexadecimal2 = 0x12_34_56_78; long hexadecimal3 = 0x88_77_66_55_44_33_22_11L; // podtržítka lze použít i při zápisu reálných konstant float f1 = -1_2.3_4f; double f2 = -1_2.3_4e10; double f3 = -1_2.3_4e1_2_3; System.out.println(decimal1); System.out.println(decimal2); System.out.println(decimal3); System.out.println(); System.out.format("%x\n", binary1); System.out.format("%x\n", binary2); System.out.format("%x\n", binary3); System.out.println(); System.out.format("%o\n", octal1); System.out.format("%o\n", octal2); System.out.format("%o\n", octal3); System.out.println(); System.out.format("%x\n", hexadecimal1); System.out.format("%x\n", hexadecimal2); System.out.format("%x\n", hexadecimal3); System.out.println(); System.out.println(f1); System.out.println(f2); System.out.println(f3); } }
5. Příkaz switch v JDK 1.0 až JDK 5
Mezi množinu několika rozšíření syntaxe i sémantiky Javy, které lze v nové verzi JDK 7 použít, patří i možnost použití řetězců v řídicím příkazu switch. Tato řídicí struktura je v Javě dostupná od samého počátku, tj. od JDK 1.0 (což je vlastně samozřejmé, neboť byla po mírných úpravách převzata z jazyků C a C++). Ovšem původně se jednalo o řídicí strukturu, v níž bylo možné za klíčovým slovem switch použít pouze celočíselný výraz a jednotlivé větve specifikované klíčovým slovem case mohly obsahovat pouze celočíselnou konstantu typu byte, short či int (nikoli však long, což je omezení dané strukturou bajtkódu!). Kromě těchto tří datových typů bylo možné použít i znakový literál, který je v Javě taktéž považován za jinak zapsanou celočíselnou šestnáctibitovou konstantu typu char. Při překladu zdrojového kódu je programová konstrukce vytvořená s využitím klíčových slov switch, case, break a default přeložena do bajtkódu, v němž jsou použity instrukce tableswitch, popř. lookupswitch, s nimiž se setkáme i v dalším textu.
Konkrétní výběr jedné z těchto instrukcí, jež se ve vygenerovaném bajtkódu skutečně použije, je proveden v závislosti na tom, která instrukce je pro daný příkaz switch vykonána efektivněji nebo která je reprezentována kratším bajtkódem (to záleží především na tom, jaké konstanty jsou uvedeny u jednotlivých větví case). V J2SE 5.0 byly možnosti řídicí konstrukce switch ve dvou směrech rozšířeny. Kromě primitivních datových typů byte, short, int a char byla přidána i podpora pro instance obalových tříd těchto typů, tj. pro instance tříd Character, Byte, Short a Integer . Tato podpora samozřejmě souvisí s autoboxingem a unboxingem, což jsou taktéž nové vlastnosti J2SE 5.0. Ovšem mnohem užitečnější bylo rozšíření příkazu switch o výčtový typ (enumeration – enum), díky čemuž bylo možné mnoho algoritmů zapsat čitelnějším a taktéž bezpečnějším způsobem, protože se z programů mohly odstranit typově nebezpečné „magické konstanty“.
6. Rychlý pohled „pod kapotu“ JDK
Zajímavé je, že syntaktické a sémantické rozšíření řídicí konstrukce switch o možnost použití výčtového typu vůbec nevedlo k nutnosti změn v bajtkódu, tj. do bajtkódu nebylo nutné přidávat žádné další typy instrukcí. Před provedením rozeskoku s využitím instrukce tableswitch nebo lookupswitch se totiž interně pro objekt výčtového typu zavolá metoda Enum.ordinal(), která vrátí ordinální (pořadové) číslo daného prvku výčtového typu. Získání tohoto celého čísla je velmi snadné, protože instance třídy odvozené od Enum jsou v celém virtuálním stroji unikátní – jedná se o jedináčky neboli singletony. Tento přístup návrhářů Javy ke všem rozšířením jazyka, který je jasně patrný i ze změn provedených v JDK 7, si můžeme ukázat na jednoduchém příkladu. Bude nás zajímat, jakým způsobem se přeloží programová konstrukce switch použitá v následujícím zdrojovém kódu:
enum T {JEDNA, DVA, TRI} public class EnumSwitchTest { public static void main(String args[]) { T x=T.DVA; int y; switch (x) { case JEDNA: y=1; break; case DVA: y=2; break; case TRI: y=3; break; default: y=0; break; } System.out.println(y); } }
Pomocným programem javap zavolaným s parametrem -c lze získat bajtkód odpovídající předchozí konstrukci (české poznámky jsem samozřejmě dopsal ručně, podobně jako jsem přidal odřádkování):
public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field T.DVA:LT; 3: astore_1 4: getstatic #3; //Field Test$1.$SwitchMap$T:[I 7: aload_1 8: invokevirtual #4; // zde se volá metoda T.ordinal() vracející číslo 1-3 11: iaload 12: tableswitch{ // rozhodovací tabulka pro hodnoty 1 až 3 1: 40; // skok na instrukci s adresou 40 2: 45; // skok na instrukci s adresou 45 3: 50; // skok na instrukci s adresou 50 default: 55 } // skok na instrukci s adresou 55 40: iconst_1 // cíl prvního skoku 41: istore_2 // uložení celočíselné konstanty 1 do lokální proměnné y 42: goto 57 // odpovídá break, tj. ukončení switch 45: iconst_2 // cíl druhého skoku 46: istore_2 // uložení celočíselné konstanty 2 do lokální proměnné y 47: goto 57 // odpovídá break, tj. ukončení switch 50: iconst_3 // cíl třetího skoku 51: istore_2 // uložení celočíselné konstanty 3 do lokální proměnné y 52: goto 57 // odpovídá break, tj. ukončení switch 55: iconst_0 // cíl posledního skoku (default) 56: istore_2 // uložení celočíselné konstanty 0 do lokální proměnné y // (skok již není zapotřebí, jsme již na adrese 57) 57: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 60: iload_2 // načtení hodnoty lokální proměnné y do zásobníku 61: invokevirtual #6; //Method java/io/PrintStream.println:(I)V 64: return }
7. Použití řetězců v příkazu switch
Nyní se opět vraťme k již zmíněné změně sémantiky příkazu switch v JDK 7, tj. k zařazení možnosti použít výraz typu řetězec (přesněji řečeno výraz, který se vyhodnotí na řetězec) v rozhodovací části tohoto příkazu a řetězcové literály v jeho jednotlivých větvích. Na toto rozšíření mnoho vývojářů čekalo vlastně již od první verze Javy, protože řetězce jsou v tomto programovacím jazyku použity poněkud chaoticky – na jednu stranu se chovají jako další primitivní datový typ (existence řetězcového literálu a operátoru +, který je jinak vyhrazený pro primitivní datové typy), na stranu druhou jsou řetězce chápány jako běžné objekty, což třeba znamená, že operátor == nemusí vždy pracovat tak, jak si především začínající programátoři v Javě myslí, protože se porovnávají reference objektů nebo řetězců ze string poolu, nikoli samotný obsah řetězců. Ostatně si postačí vyzkoušet následující příklad, v němž se porovnává hodnota načtená z jeho prvního argumentu (zadaného na příkazové řádce) s řetězcovým literálem uloženým ve string poolu:
public class T { public static void main(String[] args) { String s = (args.length > 0) ? args[0] : ""; System.out.println(s); System.out.println(s == "ahoj"); System.out.println("ahoj".equals(s)); System.out.println("ahoj".compareTo(s)); } }
Poznámka: řetězec „ahoj“ z předchozího příkladu je ve string poolu (jenž je mj. součástí souboru .class) uložen pouze jedenkrát, proto pro něj – a jen pro něj – bude operátor == pracovat podobným způsobem, jako při porovnávání hodnot primitivních datových typů. Nemožnost použití řetězců v příkazu switch komplikovala některé úlohy, například zpracování parametrů, parsing některých typů souborů (příkladem mohou být soubory typu DXF) atd. Například při psaní funkce, která pro zadané jméno měsíce vypočítá počet dní v měsíci, se běžně používá sekvence zřetězených příkazů if-then (nejedná se o jedinou možnost, je samozřejmě možné řetězec převést na hodnotu výčtového typu pomocí metody valueOf, ovšem při pohledu do zdrojových kódů se zdá, že tuto možnost programátoři z nějakého důvodu příliš často nepoužívají):
public class StringIfEqualsTest { public static final int YEAR = 2010; public static int getDaysOfMonth(String month) { if (month.equals("April") || month.equals("June") || month.equals("September") || month.equals("November")) { return 30; } if (month.equals("January") || month.equals("March") || month.equals("May") || month.equals("July") || month.equals("August") || month.equals("October") || month.equals("December")) { return 31; } if (month.equals("February")) { return YEAR % 4 == 0 && (YEAR % 100 != 0 || YEAR % 400 == 0) ? 29 : 28; } else { throw new RuntimeException("Unknown month name: " + month); } } public static void main(String[] args) { String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; for (String month : months) { int days = getDaysOfMonth(month); System.out.format("%-10s\t%d\n", month, days); } } }
Předchozí programový kód, přesněji řečeno funkce getDaysOfMonth(), však s sebou přináší několik problémů. Především je vlastní logika této funkce schována za přemírou volání metod String.equals(), což zajisté nevede k lepší čitelnosti kódu. Co je však v některých případech téměř stejně závažné, je způsob, jakým je tato funkce provedena. Při pohledu do bajtkódu můžeme vidět, že se každý člen výrazu za if skutečně přeloží do volání metody String.equals(), což je poměrně neefektivní. Například pro měsíc Únor (February) je metoda String.equals() zavolána dvanáctkrát, což v tomto případě sice nebude mít žádný velký vliv na výkonnost, ovšem kdyby byly všechny řetězce stejně dlouhé, skutečně by se provádělo porovnání znak po znaku (pokud nemají řetězce shodnou délku, nemusí se porovnání samozřejmě provádět – viz též zdrojové kódy třídy String).
S využitím nové sémantiky příkazu switch je však možné stejný program napsat mnohem čitelněji:
public class StringSwitchTest { public static final int YEAR = 2010; public static int getDaysOfMonth(String month) { switch (month) { case "April": case "June": case "September": case "November": return 30; case "January": case "March": case "May": case "July": case "August": case "October": case "December": return 31; case "February": return YEAR % 4 == 0 && (YEAR % 100 != 0 || YEAR % 400 == 0) ? 29 : 28; default: throw new RuntimeException("Unknown month name: " + month); } } public static void main(String[] args) { String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; for (String month : months) { int days = getDaysOfMonth(month); System.out.format("%-10s\t%d\n", month, days); } } }
8. Struktura generovaného bajtkódu při použití řetězců v příkazu switch
Vraťme se nyní k oběma demonstračním příkladům. Funkce getDaysOfMonth() z prvního příkladu (přeložitelného i na JDK 1.0) je přeložena do bajtkódu, z něhož je patrné, že se příkazy if s poměrně složitými podmínkami přeložily do sekvence instrukcí tvořících „špagetový kód“ tvořený mnoha podmíněnými skoky ifeq (branch if equal) a ifne (branch if not equal):
public static int getDaysOfMonth(java.lang.String); Code: 0: aload_0 1: ldc #2; //String April 3: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 6: ifne 36 // větvení na základě výsledku metody String.equals() 9: aload_0 10: ldc #4; //String June 12: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 15: ifne 36 // větvení na základě výsledku metody String.equals() 18: aload_0 19: ldc #5; //String September 21: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 24: ifne 36 // větvení na základě výsledku metody String.equals() 27: aload_0 28: ldc #6; //String November 30: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 33: ifeq 39 36: bipush 30 // cíl předchozích větvení - vrácení hodnoty 30 38: ireturn 39: aload_0 40: ldc #7; //String January 42: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 45: ifne 102 48: aload_0 49: ldc #8; //String March 51: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 54: ifne 102 57: aload_0 58: ldc #9; //String May 60: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 63: ifne 102 66: aload_0 67: ldc #10; //String July 69: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 72: ifne 102 75: aload_0 76: ldc #11; //String August 78: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 81: ifne 102 84: aload_0 85: ldc #12; //String October 87: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 90: ifne 102 93: aload_0 94: ldc #13; //String December 96: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 99: ifeq 105 102: bipush 31 104: ireturn 105: aload_0 106: ldc #14; //String February 108: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 111: ifeq 117 114: bipush 28 116: ireturn
Naproti tomu při čtení bajtkódu druhé funkce přeložené pomocí JDK 7, narazíme na poměrně velké odlišnosti. Především se nejdříve vypočte otisk (hash) řetězce month (otiskem je 32bitové celé číslo). Tento otisk je použit v instrukci lookupswitch při porovnávání s otisky řetězcových literálů. Zde je důležité především to, že tyto otisky jsou vypočteny již v době překladu, tj. v době běhu se jimi již JVM nemusí zdržovat. Pokud je otisk řetězce month shodný s otiskem nějakého literálu, je proveden skok do větve, v níž se zavolá metoda String.equals(), protože shoda otisků řetězců samozřejmě nemusí znamenat to, že jsou řetězce skutečně identické (jinými slovy – při hešování nutně dochází ke kolizím). Důležité je, že se metoda String.equals() zavolá maximálně jedenkrát (přesněji řečeno v našem případě maximálně jedenkrát, protože u námi použitých řetězcových literálů nedošlo ke kolizi), což je důležité, protože volání metody String.equals() je obecně mnohem pomalejší, než pouhé porovnání dvou 32bitových otisků.
V každé větvi je na zásobník uložena celočíselná konstanta, která je následně použita v instrukci tableswitch pro vrácení správné hodnoty z celé funkce. Výsledný bajtkód je sice poněkud delší, ovšem je na druhou stranu proveden mnohem rychleji, a to zejména v případech, kdy počet větví příkazu switch roste:
public static int getDaysOfMonth(java.lang.String); Code: 0: aload_0 1: astore_1 2: iconst_m1 3: istore_2 4: aload_1 5: invokevirtual #2; //Method java/lang/String.hashCode:()I 8: lookupswitch{ //12 -199248958: 275; // otisky řetězců jednotlivých měsíců + cílová adresa skoku -162006966: 172; -25881423: 144; 77125: 200; 2320440: 215; 2320482: 130; 43165376: 245; 63478374: 116; 74113571: 186; 626483269: 260; 1703773522: 158; 1972131363: 230; default: 287 } 116: aload_1 117: ldc #3; //String April 119: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 122: ifeq 287 125: iconst_0 126: istore_2 127: goto 287 130: aload_1 131: ldc #5; //String June 133: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 136: ifeq 287 139: iconst_1 140: istore_2 141: goto 287 144: aload_1 145: ldc #6; //String September 147: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 150: ifeq 287 153: iconst_2 154: istore_2 155: goto 287 158: aload_1 159: ldc #7; //String November 161: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 164: ifeq 287 167: iconst_3 168: istore_2 169: goto 287 172: aload_1 173: ldc #8; //String January 175: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 178: ifeq 287 181: iconst_4 182: istore_2 183: goto 287 186: aload_1 187: ldc #9; //String March 189: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 192: ifeq 287 195: iconst_5 196: istore_2 197: goto 287 200: aload_1 201: ldc #10; //String May 203: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 206: ifeq 287 209: bipush 6 211: istore_2 212: goto 287 215: aload_1 216: ldc #11; //String July 218: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 221: ifeq 287 224: bipush 7 226: istore_2 227: goto 287 230: aload_1 231: ldc #12; //String August 233: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 236: ifeq 287 239: bipush 8 241: istore_2 242: goto 287 245: aload_1 246: ldc #13; //String October 248: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 251: ifeq 287 254: bipush 9 256: istore_2 257: goto 287 260: aload_1 261: ldc #14; //String December 263: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 266: ifeq 287 269: bipush 10 271: istore_2 272: goto 287 275: aload_1 276: ldc #15; //String February 278: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 281: ifeq 287 284: bipush 11 286: istore_2 287: iload_2 288: tableswitch{ //0 to 11 0: 352; // zde již máme všech 13 cílů skoku: 12 měsíců + větev default 1: 352; 2: 352; 3: 352; 4: 355; 5: 355; 6: 355; 7: 355; 8: 355; 9: 355; 10: 355; 11: 358; default: 361 } 352: bipush 30 354: ireturn 355: bipush 31 357: ireturn 358: bipush 28 360: ireturn
9. Odkazy na Internetu
- OpenJDK Source Releases
http://download.java.net/openjdk/jdk7/ - Java Platform, Standard Edition 7 Source Snapshot Releases
http://download.java.net/jdk7/ - 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/ - 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/