Programavimas

Pagaliau apibrėžtos ir parodytos bandymo sąlygos

Sveiki atvykę į kitą Po gaubtu. Šis stulpelis leidžia „Java“ kūrėjams pažvelgti į paslaptingus mechanizmus, spustelėjusius ir sukančius po veikiančiomis „Java“ programomis. Šio mėnesio straipsnyje tęsiama „Java“ virtualiosios mašinos (JVM) baitų kodo instrukcijų rinkinio aptarimas. Pagrindinis dėmesys skiriamas JVM elgesiui pagaliau šiems sakiniams aktualūs sakiniai ir jų kodai.

Galiausiai: kažkas, kuo džiaugtis

Kai „Java“ virtuali mašina vykdo „Java“ programą atstovaujančius baitų kodus, ji gali išeiti iš kodo bloko - sakinių tarp dviejų derančių garbanotų petnešėlių - vienu iš kelių būdų. Viena vertus, JVM paprasčiausiai galėjo įvykdyti kodo bloko uždaromąjį garbanotąjį petnešą. Arba jis gali susidurti su pertrauka, tęsti arba grąžinti sakinį, dėl kurio jis iššoka iš kodo bloko iš kažkur bloko viduryje. Galiausiai gali būti padaryta išimtis, dėl kurios JVM pereina prie atitinkančios sugavimo sąlygos arba, jei nėra atitinkančios sugavimo sąlygos, nutraukia giją. Kai šie potencialūs išėjimo taškai yra viename kodo bloke, pageidautina turėti paprastą būdą išreikšti, kad kažkas įvyko, kad ir kaip būtų išjungtas kodo blokas. Java kalboje toks noras išreiškiamas a pabandyk pagaliau sąlyga.

Norėdami naudoti a pabandyk pagaliau sąlyga:

  • pridėti a bandyti blokuoti kodą, kuriame yra keli išėjimo taškai, ir

  • įdėti a pagaliau blokuoti kodą, kuris turi atsitikti, kad ir kaip bandyti užblokuotas.

Pavyzdžiui:

pabandykite {// Kodo blokas su keliais išėjimo taškais} galiausiai {// Kodo blokas, kuris visada vykdomas, kai bandymo blokas yra išjungtas, // nesvarbu, kaip bandomasis blokas yra išeinamas} 

Jei tokių turite pagauti su sąlyga susijusios sąlygos bandyti blokuoti, turite įdėti pagaliau sąlyga po visų pagauti sąlygos, kaip nurodyta:

pabandykite {// Blokuoti kodą su keliais išėjimo taškais} gaudyti (Cold e) {System.out.println ("Caught cold!"); } gaudyti (APopFly e) {System.out.println ("Pagavo popmuziką!"); } pagauti (SomeonesEye e) {System.out.println ("Patraukė kieno nors akį!"); } pagaliau {// Kodo blokas, kuris visada vykdomas, kai bandomasis blokas yra išjungtas, // nesvarbu, kaip bandomasis blokas yra išjungtas. System.out.println ("Ar tai yra kuo džiaugtis?"); } 

Jei vykdant kodą per bandyti bloką, išmetama išimtis, kurią tvarko a pagauti sąlyga, susijusi su bandyti blokas, pagaliau sąlyga bus įvykdyta po pagauti sąlyga. Pavyzdžiui, jei a Šalta išimtis metama vykdant pareiškimus (neparodyti) bandyti aukščiau esančiame laukelyje šis tekstas bus įrašytas į standartinę išvestį:

Pagavo šaltas! Ar tai yra kuo džiaugtis? 

Pabandykite pagaliau baitų koduose

Baitų koduose pagaliau sąlygos metodo metu veikia kaip miniatiūrinės paprogramės. Kiekviename išėjimo taške, esančiame a bandyti blokas ir su juo susijęs pagauti sąlygos, miniatiūrinis paprogramė, atitinkanti pagaliau vadinama sąlyga. Po to, kai pagaliau sąlyga baigiama - tol, kol ji baigiama vykdant paskutinį pagaliau sąlyga, o ne metant išimtį ar vykdant grąžą, tęsti ar pertraukti - grįžta pati miniatiūrinė paprogramė. Vykdymas tęsiasi tiesiai už taško, kuriame pirmiausia buvo iškviestas miniatiūrinis paprogramė, taigi bandyti bloką galima išeiti tinkamu būdu.

Opcode, dėl kurio JVM pereina į miniatiūrinį paprogramę, yra jsr instrukcija. jsr instrukcijai reikalingas dviejų baitų operandas, poslinkis nuo operacijos vietos jsr instrukcija, kur prasideda miniatiūrinis paprogramė. Antrasis variantas jsr instrukcija yra jsr_w, atliekanti tą pačią funkciją kaip jsr bet užima platų (keturių baitų) operandą. Kai JVM susiduria a jsr arba jsr_w instrukciją, jis stumia grįžimo adresą į kaminą, tada tęsia vykdymą miniatiūrinio paprogramės pradžioje. Grąžinimo adresas yra baito kodo, iš karto po jo, poslinkis jsr arba jsr_w instrukcija ir jos operandai.

Užbaigus miniatiūrinį paprogramę, jis iškviečia ret instrukcija, kuri grįžta iš paprogramės. ret instrukcija ima vieną operandą, indeksą į vietinius kintamuosius, kur saugomas grąžinimo adresas. Opcodes, su kuriais susiduriama pagaliau sąlygos yra apibendrintos šioje lentelėje:

Pagaliau sąlygos
„Opcode“Operandas (-ai)apibūdinimas
jsrbranchbyte1, branchbyte2stumia grįžimo adresą, šakojasi atskaityti
jsr_wbranchbyte1, branchbyte2, branchbyte3, šakos baitas4stumia grįžimo adresą, išsišakoja į platų poslinkį
retindeksasgrįžta į adresą, išsaugotą vietiniame kintamųjų rodyklėje

Nepainiokite miniatiūrinio paprogramės su „Java“ metodu. „Java“ metodai naudoja kitokį instrukcijų rinkinį. Tokios instrukcijos kaip invokevirtualus arba invokenonvirtual sukelti „Java“ metodą ir tokias instrukcijas kaip grįžti, grįžimasarba grįšiu sukelti „Java“ metodo grįžimą. jsr instrukcija nesukelia Java metodo. Vietoj to, tai sukelia tą patį metodą pereinant prie kito opcode. Taip pat ret instrukcija negrįžta iš metodo; veikiau grįžta prie opkodo tuo pačiu metodu, kuris iškart seka paskambinimą jsr instrukcija ir jos operandai. Baitų kodai, įgyvendinantys a pagaliau sakinys yra vadinamas miniatiūriniu paprogramiu, nes jie veikia kaip mažas paprogramė vieno metodo baitų kodų sraute.

Galite pagalvoti, kad ret instrukcija turėtų iššokti grąžinimo adresą iš kamino, nes būtent ten jį nustūmė jsr instrukcija. Bet taip nėra. Vietoj to, kiekvienos paprogramės pradžioje grąžinimo adresas iššoka nuo kamino viršaus ir saugomas vietiniame kintamajame - tame pačiame vietiniame kintamajame, iš kurio ret instrukcija vėliau gauna. Šis asimetriškas darbo su grįžimo adresu būdas yra būtinas, nes galiausiai sąlygos (taigi ir miniatiūrinės paprogramės) gali sukelti išimčių arba įtraukti grįžti, pertraukaarba Tęsti pareiškimus. Dėl šios galimybės papildomas grąžinimo adresas, kurį jsr instrukcija turi būti nedelsiant pašalinta iš kamino, todėl jos vis tiek nebus, jei pagaliau sąlyga išeina su a pertrauka, Tęsti, grįžti, arba išmesta išimtis. Todėl grąžinimo adresas bet kurio pradžioje saugomas vietiniame kintamajame pagaliau miniatiūrinio punkto paprogramė.

Kaip iliustraciją, apsvarstykite šį kodą, kuriame yra a pagaliau sąlyga, kuri išeina su lūžio sakiniu. Šio kodo rezultatas yra tas, kad, nepriklausomai nuo metodo perduoto parametro bVal staigmenaProgramuotojas (), metodas grąžinamas melagingas:

 statinis loginis staigmenaTheProgrammer (boolean bVal) {while (bVal) {try {return true; } pagaliau {pertrauka; }} return false; } 

Aukščiau pateiktame pavyzdyje parodyta, kodėl grąžinimo adresas turi būti saugomas vietiniame kintamajame pagaliau sąlyga. Nes pagaliau sąlyga išeina su pertrauka, ji niekada nevykdo ret instrukcija. Todėl JVM niekada negrįžta baigti „grįžti tiesa"Vietoj to, jis tiesiog eina į priekį su pertrauka ir nuleidžia žemyn už uždaromos garbanotos petnešos kol pareiškimas. Kitas teiginys yra „grąžinti klaidingą", būtent tai ir daro JVM.

Elgesys, kurį rodo a pagaliau sąlyga, kuri išeina su a pertrauka taip pat rodo pagaliau sąlygos, kurios išeina su a grįžti arba Tęsti, arba metant išimtį. Jeigu pagaliau išlyga išplaukia dėl bet kurios iš šių priežasčių, ret instrukcija pagaliau sąlyga niekada nevykdoma. Nes ret negarantuojama, kad instrukcija bus vykdoma, ja negalima remtis norint pašalinti grąžinimo adresą iš rietuvės. Todėl grąžinimo adresas yra saugomas vietiniame kintamajame pagaliau miniatiūrinio punkto paprogramė.

Norėdami gauti išsamų pavyzdį, apsvarstykite šį metodą, kuriame yra a bandyti blokas su dviem išėjimo taškais. Šiame pavyzdyje abu išėjimo taškai yra grįžti teiginiai:

 static int giveMeThatOldFashionedBoolean (boolean bVal) {try {if (bVal) {grąžinti 1; } grąžinti 0; } pagaliau {System.out.println ("Pasenau."); }} 

Aukščiau pateiktas metodas sudaro šiuos baitekodus:

// Pabandymo bloko baitų kodų seka: 0 iload_0 // Stumti vietinį kintamąjį 0 (arg perduotas kaip daliklis) 1 ifeq 11 // Stumti vietinį kintamąjį 1 (arg perduotas kaip dividendas) 4 iconst_1 // Push int 1 5 istore_3 // Įveskite int (1), išsaugokite 3 vietiniame kintamajame 6 jsr 24 // Pereikite prie mini paprogramės, kad gautumėte paskutinį 9 straipsnį iload_3 // Paspauskite 3 vietinį kintamąjį (1) 10 ireturn // Grįžkite int viršuje kaminas (1) 11 iconst_0 // Push int 0 12 istore_3 // Pop an int (the 0), store in local kintamasis 3 13 jsr 24 // Pereikite prie mini paprogramės, kad galiausiai galėtumėte sakyti 16 iload_3 // Push local 3 kintamasis (0) 17 grįžimas // Grįžimas int ant kamino viršaus (0) // Baito kodo seka sugavimo sąlygai, kuri užfiksuoja bet kokią išimtį //, išmestą iš bandymo bloko. 18 astore_1 // Įtraukite nuorodą į išmestą išimtį, saugokite // į 1 vietinį kintamąjį 19 jsr 24 // Peršokite į mini paprogramę, kad gautumėte paskutinį 22 punktą aload_1 // Pastumkite nuorodą (į metamą išimtį) iš // 1 kintamasis kintamasis 23 athrow // Pakartokite tą pačią išimtį // Miniatiūrinis paprogramis, įgyvendinantis galutinį bloką. 24 astore_2 // Parodykite grąžinimo adresą, išsaugokite jį 2 vietiniame kintamajame 25 getstatic # 8 // Gaukite nuorodą į java.lang.System.out 28 ldc # 1 // Išstumkite iš pastovaus telkinio 30 invokevirtual # 7 // Invoke System.out.println () 33 ret 2 // Grįžti į grįžimo adresą, saugomą 2 vietiniame kintamajame 

Už bytecodes bandyti blokas apima du jsr instrukcijas. Kitas jsr instrukcija yra pagauti sąlyga. pagauti sąlyga pridedama kompiliatoriaus, nes jei vykdant bandyti blokuoti, galutinis blokas vis tiek turi būti vykdomas. Todėl pagauti sąlyga tik remiasi miniatiūriniu paprogramiu, kuris reprezentuoja pagaliau sąlygą, tada vėl meta tą pačią išimtį. Išimčių lentelė giveMeThatOldFashionedBoolean () metodas, parodytas žemiau, rodo, kad bet kokia išimtis, įmesta tarp 0 ir 17 adresų (įskaitant visus bytec kodus, kurie įgyvendina bandyti bloką) tvarko pagauti sąlyga, prasidedanti 18 adresu.

Išimčių lentelė: nuo tikslo tipo 0 18 18 bet kuri 

Baitekodai pagaliau sąlyga prasideda iškeliant grįžimo adresą iš kamino ir įrašant jį į antrąjį vietinį kintamąjį. Pasibaigus pagaliau sąlyga, ret instrukcija grąžinimo adresą paima iš tinkamos vietos, antrasis vietinis kintamasis.

„HopAround“: „Java“ virtualios mašinos modeliavimas

Žemiau esanti programėlė demonstruoja „Java“ virtualią mašiną, vykdančią baitų kodų seką. Modeliavimo baitų kodų seką sukūrė javac kompiliatorius apynių aplink () toliau nurodytos klasės metodas:

klasė Klounas {static int hopAround () {int i = 0; o (tiesa) {pabandykite {pabandykite {i = 1; } pagaliau {// pirmasis galutinis sakinys i = 2; } i = 3; grįžti i; // tai niekada nebaigiama, nes tęsinys} galiausiai {// antrasis galutinis sakinys if (i == 3) {tęsti; // šis tęsinys pakeičia grąžinimo teiginį}}}}} 

Sukurtus baitų kodus javacapynys (aplink) metodas parodytas žemiau: