Ohjelmointi

Yritä lopuksi lausekkeet määritelty ja osoitettu

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ä, ja

  • laittaa sisään lopulta estä koodi, jonka täytyy tapahtua riippumatta siitä, miten yrittää 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:

Lopuksi lausekkeet
OpcodeOperandi (t)Kuvaus
jsrhaara tavu1, haara tavu2työntää paluuosoitteen, haarautuu tasaamaan
jsr_whaarautuma1, haara2, haara3, haara4työntää paluuosoitteen, haarautuu laajaan siirtymään
retindeksipalaa 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, taukotai 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, palatatai 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 tottaSen 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 jatkaatai 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: