Programmēšana

Izmēģinājuma beigās noteiktas un demonstrētas klauzulas

Laipni lūdzam citā Zem kapuces. Šī sleja ļauj Java izstrādātājiem ieskatu noslēpumainajos mehānismos, kas noklikšķina un virpuļo zem viņu darbojošajām Java programmām. Šomēnes rakstā tiek turpināta Java virtuālās mašīnas (JVM) baitu kodu instrukciju kopas apspriešana. Tās uzmanības centrā ir veids, kādā JVM rīkojas beidzot klauzulas un bytecodes, kas attiecas uz šiem klauzulām.

Visbeidzot: Kaut ko uzmundrināt

Tā kā Java virtuālā mašīna izpilda bytecodus, kas attēlo Java programmu, tā var iziet no koda bloka - paziņojumiem starp divām atbilstošām cirtainajām iekavām - vienā no vairākiem veidiem. Pirmkārt, JVM vienkārši varēja izpildīt gar kodu bloka aizvērto cirtaino stiprinājumu. Vai arī tas var sastapt pārrāvuma, turpinājuma vai atgriešanās paziņojumu, kas liek tam izlēkt no koda bloka no kaut kur bloka vidus. Visbeidzot, varētu tikt izlikts izņēmums, kas liek JVM pāriet uz atbilstošu nozvejas klauzulu vai, ja nav atbilstošas ​​nozvejas klauzulas, pārtraukt pavedienu. Tā kā šie potenciālie izejas punkti pastāv vienā koda blokā, ir vēlams, lai būtu vienkāršs veids, kā paust, ka kaut kas noticis neatkarīgi no tā, kā tiek iziets no koda bloka. Java valodā šāda vēlme tiek izteikta ar a mēģiniet beidzot klauzula.

Lai izmantotu a mēģiniet beidzot klauzula:

  • pievienot a mēģiniet bloķēt kodu, kuram ir vairāki izejas punkti, un

  • ielikt a beidzot bloķēt kodu, kuram jānotiek neatkarīgi no tā, kā mēģiniet bloks ir iziets.

Piemēram:

mēģiniet {// koda bloks ar vairākiem izejas punktiem} visbeidzot {// koda bloks, kas vienmēr tiek izpildīts, izejot no mēģinājuma bloka, // neatkarīgi no mēģinājuma bloka izejas} 

Ja jums tādas ir noķert klauzulas, kas saistītas ar mēģiniet bloķēt, jums ir jāievieto beidzot klauzula pēc visiem noķert klauzulas, kā:

mēģiniet {// koda bloks ar vairākiem izejas punktiem} catch (Cold e) {System.out.println ("Caught auksts!"); } catch (APopFly e) {System.out.println ("Noķēris popmušu!"); } catch (SomeonesEye e) {System.out.println ("Uzķēru kādam acis!"); } visbeidzot {// Kodu bloks, kas vienmēr tiek izpildīts, izejot no mēģinājuma bloka, // neatkarīgi no mēģinājuma bloka izejas. System.out.println ("Vai tas ir par ko uzmundrināt?"); } 

Ja koda izpildes laikā a mēģiniet bloķēt, tiek izmests izņēmums, kuru apstrādā a noķert klauzula, kas saistīta ar mēģiniet bloķēt beidzot klauzula tiks izpildīta pēc noķert klauzula. Piemēram, ja a Auksts izņēmums tiek izmests paziņojumu izpildes laikā (nav parādīts) mēģiniet iepriekšminētajā blokā standarta izvadē tiks ierakstīts šāds teksts:

Noķēris auksts! Vai tas ir par ko uzmundrināt? 

Mēģiniet beidzot klauzulas baitkodos

Baitkodos beidzot klauzulas darbojas kā miniatūras apakšprogrammas metodes ietvaros. Katrā izejas punktā iekšpusē a mēģiniet bloks un ar to saistītais noķert klauzulas, miniatūra apakšprogramma, kas atbilst beidzot klauzula tiek saukta. Pēc tam, kad beidzot klauzula tiek pabeigta - kamēr tā tiek pabeigta, izpildot iepriekšējo priekšrakstu beidzot klauzulu, nevis izmetot izņēmumu vai izpildot atgriešanos, turpinot vai pārtraucot - atgriežas pati miniatūra apakšprogramma. Izpildīšana turpinās tieši pāri vietai, kur vispirms tika izsaukta miniatūra apakšprogramma, tāpēc mēģiniet bloku var iziet atbilstošā veidā.

Opkods, kas liek JVM pāriet uz miniatūru apakšprogrammu, ir jsr instrukcija. The jsr instrukcija aizņem divu baitu operandu, nobīdi no operācijas vietas jsr instrukcija, kur sākas miniatūra apakšprogramma. Otrais variants jsr instrukcija ir jsr_w, kas veic to pašu funkciju kā jsr bet aizņem plašu (četru baitu) operandu. Kad JVM sastop a jsr vai jsr_w instrukciju, tā nospiež atgriešanās adresi uz kaudzes, pēc tam turpina izpildi miniatūras apakšprogrammas sākumā. Atgriešanas adrese ir baitkoda nobīde tūlīt pēc jsr vai jsr_w instrukcija un tās operandi.

Pēc miniatūras apakšprogrammas pabeigšanas tā izsauc ret instrukcija, kas atgriežas no apakšprogrammas. The ret instrukcija ņem vienu operandu, indeksu vietējos mainīgajos, kur tiek glabāta atgriešanās adrese. Opkodi, ar kuriem nodarbojas beidzot klauzulas ir apkopotas šajā tabulā:

Visbeidzot klauzulas
OpcodeOperands (-i)Apraksts
jsrzaru baits1, zaru baits2nospiež atgriešanās adresi, filiāles kompensēt
jsr_wbranchbyte1, branchbyte2, branchbyte3, branchbyte4nospiež atgriešanās adresi, atzarojas līdz lielai nobīdei
retindekssatgriežas vietējā mainīgā indeksā saglabātajā adresē

Nejauciet miniatūru apakšprogrammu ar Java metodi. Java metodēs tiek izmantots atšķirīgs instrukciju kopums. Tādas instrukcijas kā invokevirtuāls vai invokenonvirtual izraisīt Java metodes izmantošanu un tādas instrukcijas kā atgriešanās, atgriešanāsvai atgriezties izraisīt Java metodes atgriešanos. The jsr instrukcija neizraisa Java metodes izmantošanu. Tā vietā tas izraisa lēcienu uz citu opkodu tajā pašā metodē. Tāpat arī ret instrukcija neatgriežas no metodes; drīzāk tas atgriežas pie opkoda ar to pašu metodi, kas tūlīt seko izsaukumam jsr instrukcija un tās operandi. Baitkodi, kas ievieš a beidzot klauzulu sauc par miniatūru apakšprogrammu, jo tās darbojas kā maza apakšprogramma vienas metodes baitkodu plūsmā.

Jūs varētu domāt, ka ret instrukcijai jāatstāj atgriešanās adrese no kaudzes, jo tieši tur to nospieda jsr instrukcija. Bet tā nav. Tā vietā katras apakšprogrammas sākumā atgriešanās adrese tiek izlaista no kaudzes augšdaļas un tiek saglabāta vietējā mainīgajā - tajā pašā vietējā mainīgajā, no kura ret instrukcija vēlāk to iegūst. Šis asimetriskais darba veids ar atgriešanās adresi ir nepieciešams, jo visbeidzot klauzulas (un līdz ar to arī miniatūras apakšprogrammas) pašas var mest izņēmumus vai ietvert atgriešanās, pārtraukumsvai Turpināt paziņojumi. Šīs iespējas dēļ papildu atgriešanās adrese, kuru jsr instrukcija ir nekavējoties jānoņem no kaudzes, tāpēc tā joprojām nebūs tur, ja beidzot klauzula iziet ar a pārtraukums, Turpināt, atgriešanās, vai izmests izņēmums. Tāpēc atgriešanās adrese jebkuras sākumā tiek saglabāta vietējā mainīgajā beidzot klauzulas miniatūru apakšprogrammu.

Kā ilustrāciju ņemiet vērā šādu kodu, kas ietver a beidzot klauzula, kas iziet ar pārtraukuma paziņojumu. Šī koda rezultāts ir tāds, ka neatkarīgi no parametra bVal, kas nodots metodei pārsteigumsProgrammētājs (), metode atgriežas nepatiesa:

 statiskais būla pārsteigumsTheProgrammer (boolean bVal) {while (bVal) {try {return true; } beidzot {pārtraukums; }} return false; } 

Iepriekš minētajā piemērā parādīts, kāpēc atgriešanās adrese ir jāsaglabā vietējā mainīgajā beidzot klauzula. Tāpēc ka beidzot klauzula iziet ar pārtraukumu, tā nekad neizpilda ret instrukcija. Rezultātā JVM nekad neatgriežas, lai pabeigtu "atgriezties patiess"Tā vietā, tā vienkārši iet uz priekšu ar pārtraukums un nomet lejā garām aizvērtajam cirtainajam stiprinājumam kamēr paziņojums, apgalvojums. Nākamais paziņojums iratgriezties nepatiesa, "tieši to dara JVM.

Uzvedība, ko parāda a beidzot klauzula, kas iziet ar a pārtraukums ir parādīts arī ar beidzot klauzulas, kas iziet ar a atgriešanās vai Turpināt, vai arī izmetot izņēmumu. Ja beidzot klauzula iziet jebkura no šiem iemesliem, ret instrukcija. beigās beidzot klauzula nekad netiek izpildīta. Tāpēc ka ret instrukcijas izpilde netiek garantēta, uz to nevar paļauties, lai noņemtu atgriešanās adresi no kaudzes. Tāpēc atgriešanās adrese tiek saglabāta vietējā mainīgajā beidzot klauzulas miniatūru apakšprogrammu.

Lai iegūtu pilnīgu piemēru, apsveriet šādu metodi, kas satur a mēģiniet bloķēt ar diviem izejas punktiem. Šajā piemērā abi izejas punkti ir atgriešanās paziņojumi:

 static int giveMeThatOldFashionedBoolean (būla bVal) {mēģiniet {ja (bVal) {atgriezt 1; } atgriešanās 0; } visbeidzot {System.out.println ("Got vecmodīgi."); }} 

Iepriekš minētā metode apkopo šādus baitkodus:

// Baitkodu secība mēģinājuma blokam: 0 iload_0 // Push vietējais mainīgais 0 (arg nodots kā dalītājs) 1 ifeq 11 // Push lokālais mainīgais 1 (arg nodots kā dividende) 4 iconst_1 // Push int 1 5 istore_3 // Ievietojiet int (1), uzglabājiet lokālajā mainīgajā 3 6 jsr 24 // Pārejiet uz mini apakšprogrammu, lai iegūtu pēdējo 9. klauzulu iload_3 // Nospiediet vietējo mainīgo 3 (1) 10 atgriezties // Atgriezties int augšpusē kaudze (1) 11 iconst_0 // Push int 0 12 istore_3 // Pop int (0), glabājiet lokālajā mainīgajā 3 13 jsr 24 // Pārejiet uz pēdējās 16. klauzulas mini-apakšprogrammu iload_3 // Push local mainīgais 3 (0) 17 ireturn // Atgriešanās int kaudzes augšpusē (0) // Baitkodu secība nozvejas klauzulai, kas uztver jebkāda veida izņēmumus //, kas izmests no mēģinājuma bloka. 18 astore_1 // Ievietojiet atsauci uz izmesto izņēmumu, uzglabājiet // lokālajā mainīgajā 1 19 jsr 24 // Pāriet uz mini apakšprogrammu, lai iegūtu pēdējo 22. klauzulu aload_1 // Pabīdiet atsauci (uz izmesto izņēmumu) no // lokālais mainīgais 1 23 athrow // Atkārtojiet to pašu izņēmumu // Miniatūra apakšprogramma, kas īsteno pēdējo bloku. 24 astore_2 // Atgriešanās adreses atvēršana, glabāšana lokālajā mainīgajā 2 25 getstatic # 8 // Iegūstiet atsauci uz java.lang.System.out 28 ldc # 1 // Spiediet no nemainīgā kopas 30 invokevirtual # 7 // Invoke System.out.println () 33 ret 2 // Atgriezties pie atgriešanās adreses, kas saglabāta 2. lokālajā mainīgajā 

Bitu kodi mēģiniet blokā ietilpst divi jsr instrukcijas. Cits jsr instrukcija ir ietverta noķert klauzula. The noķert klauzulu sastādītājs pievieno tāpēc, ka, ja izpildes laikā tiek izmests izņēmums mēģiniet bloķēt, galīgais bloks joprojām ir jāizpilda. Tāpēc noķert klauzula tikai atsaucas uz miniatūru apakšprogrammu, kas attēlo beidzot klauzulā, tad atkal met to pašu izņēmumu. Izņēmumu tabula giveMeThatOldFashionedBoolean () metode, kas parādīta zemāk, norāda, ka visi izņēmumi, kas izmesti starp 0 un 17 adresēm (ieskaitot visus bytecodes, ar kurām ievieš mēģiniet bloku) apstrādā noķert klauzula, kas sākas ar 18. adresi.

Izņēmumu tabula: no līdz mērķa tipam 0 18 18 jebkurš 

Bitu kodi beidzot klauzula sākas ar popping atgriešanās adresi no kaudzes un saglabājot to lokālajā mainīgajā otrajā. Pēc beigām beidzot klauzula ret instrukcija atgriežas no pareizās vietas, otrais mainīgais.

HopAround: Java virtuālās mašīnas simulācija

Zemāk esošā sīklietotne parāda Java virtuālo mašīnu, kas izpilda baitkodu secību. Baitkodu secību simulācijā izveidoja javac kompilators hopAround () zemāk redzamās klases metode:

klase Klauns {static int hopAround () {int i = 0; kamēr (taisnība) {mēģiniet {mēģiniet {i = 1; } visbeidzot {// pirmais galīgais teikums i = 2; } i = 3; atgriešanās i; // tas nekad nav pabeigts, jo turpinājums} beidzot {// otrais galīgais teikums if (i == 3) {turpināt; // šis turpinājums ignorē atgriešanās paziņojumu}}}}} 

Bytecodes, ko ģenerējis javac priekš hopAround () metode ir parādīta zemāk:

$config[zx-auto] not found$config[zx-overlay] not found