Tervetuloa toiseen erään Konepellin alle. Tämä sarake antaa Java-kehittäjille vilauksen salaperäisistä mekanismeista, jotka napsauttavat ja pyörivät heidän käynnissä olevien Java-ohjelmiensa alla. Tämän kuukauden artikkeli jatkaa keskustelua Java-virtuaalikoneen (JVM) tavukoodikäskyjoukosta. Sen painopiste on tapa, jolla JVM käsittelee lopulta
näiden lausekkeiden kannalta merkitykselliset lausekkeet ja tavukoodit.
Lopuksi: Jotain ilahduttavaa
Kun Java-virtuaalikone suorittaa Java-ohjelmaa edustavat tavukoodit, se voi poistua koodilohkosta - kahden vastaavan kiharan aaltosulkeen väliset lauseet - yhdellä useista tavoista. Ensinnäkin JVM voisi yksinkertaisesti suorittaa koodilohkon sulkevan kiharan ahdistuksen ohi. Tai se voi kohdata tauon, jatko- tai palautuslausekkeen, joka saa sen hyppäämään koodilohkosta jostakin lohkon keskiosasta. Lopuksi voidaan heittää poikkeus, joka saa JVM: n joko siirtymään vastaavaan saalislausekkeeseen tai, jos vastaavaa saalislauseketta ei ole, lopettamaan langan. Kun nämä potentiaaliset poistumispisteet ovat olemassa yhdessä koodilohkossa, on toivottavaa, että meillä on helppo tapa ilmaista, että jotain tapahtui riippumatta siitä, kuinka koodilohkosta poistutaan. Jaavassa tällainen halu ilmaistaan a: lla yritä lopulta
lauseke.
Voit käyttää a yritä lopulta
lauseke:
liittää a
yrittää
estä koodi, jolla on useita poistumispisteitä, jalaittaa sisään
lopulta
estä koodi, jonka täytyy tapahtua riippumatta siitä, mitenyrittää
lohko poistuu.
Esimerkiksi:
yritä {// Koodilohko, jossa on useita poistumispisteitä} lopuksi {// Koodilohko, joka suoritetaan aina, kun kokeilulohko poistuu, // riippumatta siitä, miten kokeilulohko poistuu}
Jos sinulla on yhtään saada kiinni
Lausekkeet, jotka liittyvät yrittää
estää, sinun on laitettava lopulta
lauseke kaikkien saada kiinni
lausekkeet, kuten:
kokeile {// koodilohko, jossa on useita poistumispisteitä} kiinni (kylmä e) {System.out.println ("kylmää!"); } catch (APopFly e) {System.out.println ("Pop-fly!"); } catch (SomeonesEye e) {System.out.println ("Otti jonkun silmän!"); } lopuksi {// Koodilohko, joka suoritetaan aina, kun kokeilulohko poistuu, // riippumatta siitä, miten kokeilulohko poistuu. System.out.println ("Onko siitä jotain kannustettavaa?"); }
Jos koodin suorittamisen aikana a yrittää
lohko, heitetään poikkeus, jonka a saada kiinni
lausekkeeseen liittyvä lauseke yrittää
estää lopulta
lauseke toteutetaan saada kiinni
lauseke. Esimerkiksi, jos a Kylmä
poikkeus heitetään suoritettaessa lausekkeita (ei esitetty) yrittää
Yllä olevassa lohkossa seuraava teksti kirjoitetaan vakiotulosteeseen:
Kiinni kylmänä! Onko siitä jotain piristettävää?
Yritä lopuksi lausekkeita tavukoodeissa
Tavukoodeissa lopulta
lausekkeet toimivat pienoiskoossa aliohjelmina menetelmän sisällä. Jokaisessa poistumispisteessä a yrittää
lohko ja siihen liittyvä saada kiinni
lausekkeet, pienikokoinen aliohjelma, joka vastaa lopulta
lauseketta kutsutaan. Jälkeen lopulta
lauseke täydentää - niin kauan kuin se täydentyy suorittamalla viimeisen lauseen ohi lopulta
lauseke, ei heittämällä poikkeusta tai suorittamalla palautusta, jatkamista tai rikkomista - miniatyyri-aliohjelma palaa itse. Toteutus jatkuu juuri sen kohdan jälkeen, jossa miniatyyrialijärjestelmää alun perin kutsuttiin, joten yrittää
lohko voidaan sulkea sopivalla tavalla.
Opkoodi, joka saa JVM: n siirtymään pienoiskoossa olevaan aliohjelmaan, on jsr ohje. jsr käsky vie kaksitavuisen operandin, siirtymän operaattorin sijainnista jsr ohjeet siitä, mistä pienoiskoossa alirutiini alkaa. Toinen variantti jsr ohje on jsr_w, joka suorittaa saman toiminnon kuin jsr mutta vie laajan (nelitavuisen) operandin. Kun JVM kohtaa a jsr tai jsr_w käskyn, se työntää paluuosoitteen pinoon ja jatkaa sitten suoritusta pienoisalirutiinin alussa. Paluuosoite on heti seuraavaa tavua seuraavan koodin poikkeama jsr tai jsr_w opetus ja sen operandit.
Pienikokoisen alirutiinin valmistuttua se käynnistää ret ohje, joka palaa alirutiinista. ret käsky vie yhden operandin, indeksin paikallisiin muuttujiin, joihin paluuosoite on tallennettu. Opkoodit, jotka käsittelevät lopulta
lausekkeet on tiivistetty seuraavaan taulukkoon:
Opcode | Operandi (t) | Kuvaus |
---|---|---|
jsr | haara tavu1, haara tavu2 | työntää paluuosoitteen, haarautuu tasaamaan |
jsr_w | haarautuma1, haara2, haara3, haara4 | työntää paluuosoitteen, haarautuu laajaan siirtymään |
ret | indeksi | palaa paikalliseen muuttujahakemistoon tallennettuun osoitteeseen |
Älä sekoita pienoiskoossa olevaa aliohjelmaa Java-menetelmään. Java-menetelmät käyttävät eri komentosarjaa. Ohjeet, kuten invokevirtuaali tai invokenonvirtual aiheuttaa Java-menetelmän, ja ohjeet, kuten palata, paluutai palaan aiheuttaa Java-menetelmän palaamisen. jsr ohje ei aiheuta Java-menetelmän käyttöä. Sen sijaan se aiheuttaa hypyn eri opkoodiin samalla menetelmällä. Samoin ret käsky ei palaa menetelmästä; pikemminkin se palaa opkoodiin samalla menetelmällä, joka seuraa välittömästi kutsua jsr opetus ja sen operandit. Tavukoodit, jotka toteuttavat a lopulta
lauseketta kutsutaan pienoiskoossa aliohjelmaksi, koska ne toimivat kuin pieni aliohjelma yhden menetelmän tavukoodivirrassa.
Saatat ajatella, että ret käskyn pitäisi pudottaa paluuosoite pinosta, koska siellä se työnsi sitä jsr ohje. Mutta se ei ole. Sen sijaan jokaisen aliohjelman alussa paluuosoite ponnahtaa pinon yläosasta ja tallennetaan paikalliseen muuttujaan - samaan paikalliseen muuttujaan, josta ret ohjeet saavat sen myöhemmin. Tämä epäsymmetrinen tapa palata osoitteen kanssa on välttämätöntä, koska lopulta lausekkeet (ja siten pienikokoiset aliohjelmat) itse voivat antaa poikkeuksia tai sisällyttää palata
, tauko
tai jatkaa
lausunnot. Tämän mahdollisuuden takia ylimääräinen palautusosoite, joka työnnettiin pinoon jsr ohje on poistettava pinosta heti, joten se ei ole vielä olemassa, jos lopulta
lauseke poistuu a: lla tauko
, jatkaa
, palata
tai heitetty poikkeus. Siksi palautusosoite tallennetaan paikalliseen muuttujaan minkä tahansa alussa lopulta
lausekkeen pienikokoinen aliohjelma.
Harkitse esimerkkinä seuraavaa koodia, joka sisältää a lopulta
lauseke, joka poistuu break-lauseella. Tämän koodin tulos on, että riippumatta menetelmälle välitetystä parametrista bVal yllätysTheProgrammer ()
, menetelmä palaa väärä
:
staattinen looginen yllätysTheProgrammer (boolen bVal) {while (bVal) {try {return true; } vihdoin {tauko; }} return false; }
Yllä oleva esimerkki osoittaa, miksi palautusosoite on tallennettava paikalliseen muuttujaan lopulta
lauseke. Koska lopulta
lauseke poistuu tauolla, se ei koskaan toteuta ret ohje. Tämän seurauksena JVM ei koskaan palaa viimeistelemään "palaa totta
Sen sijaan se vain menee eteenpäin tauko
ja putoaa alas sillä aikaa
lausunto. Seuraava lausunto on "palauta väärä
, "mitä juuri JVM tekee.
A. Osoittama käyttäytyminen lopulta
lauseke, joka poistuu a: lla tauko
näkyy myös lopulta
lausekkeet, jotka poistuvat a palata
tai jatkaa
tai heittämällä poikkeus. Jos lopulta
- lauseke poistuu jostakin näistä syistä, ret ohjeet lopussa lopulta
lauseketta ei koskaan suoriteta. Koska ret käskyn suorittamista ei taata, siihen ei voida luottaa poisto-osoitteen poistamiseen pinosta. Siksi palautusosoite tallennetaan paikalliseen muuttujaan lopulta
lausekkeen pienikokoinen aliohjelma.
Tarkka esimerkki seuraavasta menetelmästä, joka sisältää a yrittää
lohko kahdella poistumispisteellä. Tässä esimerkissä molemmat poistumispisteet ovat palata
lausunnot:
staattinen int annaMeThatOldFashionedBoolean (boolen bVal) {try {if (bVal) {return 1; } paluu 0; } lopuksi {System.out.println ("Vanhanaikaista."); }}
Yllä oleva menetelmä kokoaa seuraavat tavukoodit:
// Kokeilulohkon tavukoodisekvenssi: 0 iload_0 // Työnnä paikallinen muuttuja 0 (arg välitetään jakajana) 1 ifeq 11 // Työnnä paikallinen muuttuja 1 (arg välitetään osinkona) 4 iconst_1 // Paina int 1 5 istore_3 // Pop int (1), tallenna paikalliseen muuttujaan 3 6 jsr 24 // Siirry viimeiseen lausekkeeseen 9 siirtyvään minialijärjestelmään iload_3 // Työnnä paikallinen muuttuja 3 (1) 10 paluu // Palaa int pino (1) 11 iconst_0 // Push int 0 12 istore_3 // Pop an int (the 0), tallenna paikalliseen muuttujaan 3 13 jsr 24 // Siirry viimeisen lausekkeen 16 minialijärjestelmään iload_3 // Push local muuttuja 3 (0) 17 ireturn // Paluu int pinon päälle (0) // Tavoitekoodisekvenssi saalislausekkeelle, joka saa kiinni kaikenlaisen poikkeuksen //, joka heitetään try-lohkosta. 18 astore_1 // Lisää viite heitetylle poikkeukselle, tallenna // paikalliseen muuttujaan 1 19 jsr 24 // Siirry viimeisen lausekkeen 22 alalohkoon aload_1 // Työnnä viite (heitetylle poikkeukselle) kohdasta // paikallinen muuttuja 1 23 athrow // Toista sama poikkeus // Pienikokoinen aliohjelma, joka toteuttaa viimeisen lohkon. 24 astore_2 // Lisää palautusosoite, tallenna se paikalliseen muuttujaan 2 25 getstatic # 8 // Hae viittaus java.lang.System.out 28 ldc # 1 // Työnnä vakioalueesta 30 invokevirtual # 7 // Invoke System.out.println () 33 ret 2 // Paluu paikalliseen muuttujaan 2 tallennettuun paluuosoitteeseen
. - tavukoodit yrittää
lohko sisältää kaksi jsr ohjeet. Toinen jsr ohje sisältyy saada kiinni
lauseke. saada kiinni
Kääntäjä on lisännyt lausekkeen, koska jos suoritetaan poikkeus yrittää
lohko, viimeinen lohko on silti suoritettava. Siksi saada kiinni
-lauseke vain vetoaa miniatyyri-alirutiiniin, joka edustaa lopulta
lauseke, sitten heittää saman poikkeuksen uudelleen. Poikkeustaulukko giveMeThatOldFashionedBoolean ()
alla esitetty menetelmä osoittaa, että kaikki poikkeukset, jotka on heitetty osoitteiden 0 ja 17 väliin (mukaan lukien kaikki tavukoodit, jotka toteuttavat yrittää
lohko) käsittelee saada kiinni
lauseke, joka alkaa osoitteesta 18.
Poikkeustaulukko: alkaen kohdetyyppiin 0 18 18 mikä tahansa
Tavukoodeja n lopulta
lauseke aloittaa avaamalla palautusosoite pinosta ja tallentamalla se paikalliseen muuttujaan kaksi. Lopussa lopulta
lauseke, ret käsky ottaa paluuosoitteen oikeasta paikasta, paikallinen muuttuja kaksi.
HopAround: Java-virtuaalikoneen simulointi
Alla oleva sovelma osoittaa Java-virtuaalikoneen suorittavan tavujärjestyssarjan. Simulaation tavukoodisekvenssi luotiin javac
kääntäjä hopAround ()
alla olevan luokan menetelmä:
luokka Pelle {staattinen int hopAround () {int i = 0; while (true) {yritä {kokeile {i = 1; } lopuksi {// ensimmäinen viimeinen lauseke i = 2; } i = 3; paluu i; // tämä ei koskaan toteudu, koska jatka} lopuksi {// toinen viimeinen lause, jos (i == 3) {jatka; // tämä jatkuu ohittaa palautuslausekkeen}}}}}
Luodut tavukoodit javac
varten hopAround ()
menetelmä on esitetty alla: