Programiranje

Določene in dokazane klavzule o poskusih

Dobrodošli v drugem delu Pod pokrovom. Ta stolpec daje razvijalcem Java vpogled v skrivnostne mehanizme, ki kliknejo in se vrtijo pod njihovimi programi Java. V tem mesecu se nadaljuje razprava o naboru bajt kod navideznega stroja Java (JVM). Njegov poudarek je na načinu ravnanja JVM končno in bajt kod, ki so pomembni za te klavzule.

Končno: Nekaj ​​za razveseljevanje

Ko Java navidezni stroj izvrši bajtode, ki predstavljajo program Java, lahko na en od več načinov zapusti blok kode - stavke med dvema ujemajočima se zavitima oklepajema. Prvič, JVM bi lahko preprosto izvedel mimo zapiralne oklepaje bloka kode. Lahko pa naleti na izjavo o prekinitvi, nadaljevanju ali vrnitvi, zaradi katere skoči iz bloka kode od nekje na sredini bloka. Končno bi lahko vrgli izjemo, zaradi katere JVM skoči na ujemajočo se ulovno klavzulo ali, če ni ujemajoče se ulovne klavzule, prekine nit. S temi potencialnimi izhodnimi točkami, ki obstajajo znotraj enega bloka kode, je zaželeno, da na preprost način izrazimo, da se je nekaj zgodilo, ne glede na to, kako izstopimo iz bloka kode. V Javi je taka želja izražena z poskusite končno klavzulo.

Za uporabo a poskusite končno klavzula:

  • priloži v a poskusite blokira kodo, ki ima več izhodnih točk, in

  • dal v končno blokira kodo, ki se mora zgoditi ne glede na to, kako poskusite blok je zaprt.

Na primer:

poskusite {// blok kode z več izhodnimi točkami} končno {// blok kode, ki se vedno izvrši, ko je izhodni blok izpuščen, // ne glede na to, kako je izhodni blok izpuščen} 

Če jih imate ulov klavzule, povezane z poskusite blok, morate postaviti končno klavzula po vseh ulov klavzule, kot v:

poskusite {// blok kode z več izstopnimi točkami} catch (Cold e) {System.out.println ("Prestrašen!"); } catch (APopFly e) {System.out.println ("Ujeti pop muho!"); } catch (SomeonesEye e) {System.out.println ("Ujel nekoga!"); } končno {// Blok kode, ki se vedno izvede, ko izstopi blok poskus, // ne glede na to, kako blok poskusa izstopi. System.out.println ("Ali je zaradi tega treba navijati?"); } 

Če med izvajanjem kode v poskusite blok, vrže se izjema, ki jo obravnava ulov klavzula, povezana z poskusite blok, končno klavzula bo izvršena po ulov klavzulo. Na primer, če a Hladno izjema se vrže med izvajanjem stavkov (ni prikazano) v poskusite v zgornjem bloku bi se v standardni izhod zapisalo naslednje besedilo:

Prehlajeni! Je to nekaj za razveseljevanje? 

Klavzule "poskusi končno" v bajtkodah

V bajtkodah končno klavzule delujejo kot miniaturne podprograme znotraj metode. Na vsaki izstopni točki znotraj a poskusite blok in z njim povezani ulov klavzule, miniaturna podprogram, ki ustreza končno se imenuje klavzula. Po končno klavzula dokonča - dokler se dokonča z izvajanjem preteklega zadnjega stavka v končno , ne z vrnitvijo izjeme ali izvedbo vrnitve, nadaljevanja ali preloma - miniaturna podprogram se vrne sam. Izvedba se nadaljuje tik pred točko, kjer je bila najprej imenovana miniaturna podprogram, torej poskusite blok lahko zapustite na ustrezen način.

Opcode, zaradi katerega JVM preskoči na miniaturno podprogram, je jsr navodila. The jsr navodilo sprejme dvobajtni operand, odmik od lokacije jsr navodila, kje se začne miniaturna podprogram. Druga različica jsr navodilo je jsr_w, ki opravlja enako funkcijo kot jsr vendar zavzame širok (štiribajtni) operand. Ko JVM naleti na a jsr ali jsr_w navodilo potisne povratni naslov na sklad, nato pa nadaljuje izvajanje na začetku miniaturne podprograma. Vrnjeni naslov je odmik bajtke takoj za jsr ali jsr_w navodila in njegovi operandi.

Ko se miniaturna podprogram zaključi, prikliče ponovitev navodilo, ki se vrne iz podprograma. The ponovitev navodilo sprejme en operand, indeks lokalnih spremenljivk, kjer je shranjen povratni naslov. Opcode, ki obravnavajo končno klavzule so povzete v naslednji tabeli:

Končno klavzule
OpcodeOperand (i)Opis
jsrbranchbyte1, branchbyte2potisne povratni naslov, veje pobotajo
jsr_wbranchbyte1, branchbyte2, branchbyte3, branchbyte4potisne povratni naslov, razveja se v velik odmik
ponovitevindeksvrne se na naslov, shranjen v indeksu lokalne spremenljivke

Ne mešajte miniaturne podprograme z metodo Java. Java metode uporabljajo drugačen nabor navodil. Navodila, kot so invokevirtual ali invokenonvirtual povzroči priklic metode Java in navodil, kot je vrnitev, areturn, ali ireturn povzroči vrnitev metode Java. The jsr navodilo ne povzroči priklica metode Java. Namesto tega povzroči preskok na drugo opcode znotraj iste metode. Prav tako ponovitev navodilo se ne vrne iz metode; namesto tega se vrne nazaj v kodo opcij po isti metodi, ki takoj sledi klicanju jsr navodila in njegovi operandi. Bytecode, ki izvajajo a končno klavzulo imenujemo miniaturna podprogram, ker deluje kot majhen podprogram znotraj toka bajt kod ene same metode.

Morda mislite, da ponovitev navodilo naj vrne naslov vrnitve iz sklada, ker ga je tja potisnil jsr navodila. Ampak ne. Namesto tega se na začetku vsake podprograme povratni naslov izstreli z vrha sklada in shrani v lokalno spremenljivko - isto lokalno spremenljivko, iz katere je ponovitev navodila dobijo pozneje. Ta asimetričen način dela z naslovom za vrnitev je potreben, ker lahko končno klavzule (in zato miniaturne podprograme) povzročijo izjeme ali vključujejo vrnitev, odmor, ali nadaljujte izjave. Zaradi te možnosti je dodatni povratni naslov, ki ga je na sklad potisnil jsr navodilo je treba takoj odstraniti iz sklada, zato ga še vedno ne bo, če končno klavzula izstopa z odmor, nadaljujte, vrnitevali vržena izjema. Zato se povratni naslov shrani v lokalno spremenljivko na začetku katerega koli končno miniaturna podprogram klavzule.

Za ponazoritev si oglejte naslednjo kodo, ki vključuje a končno klavzula, ki izstopi s stavkom break. Rezultat te kode je, ne glede na parameter bVal, ki je bil poslan metodi surpriseTheProgrammer (), metoda vrne napačno:

 statično logično presenečenjeTheProgrammer (logični bVal) {while (bVal) {try {return true; } končno {odmor; }} vrne false; } 

Zgornji primer prikazuje, zakaj je treba povratni naslov shraniti v lokalno spremenljivko na začetku datoteke končno klavzulo. Zaradi končno klavzula izstopi s prekinitvijo, nikoli ne izvrši ponovitev navodila. Kot rezultat, se JVM nikoli ne vrne, da bi dokončal "vrni se res". Namesto tega gre samo naprej odmor in se spusti mimo zaključne skodrane ograde medtem izjavo. Naslednja izjava je "vrni false, "kar natančno počne JVM.

Vedenje, ki ga kaže a končno klavzula, ki izstopa z odmor prikazuje tudi končno klavzule, ki izstopajo z vrnitev ali nadaljujteali z vrnitvijo izjeme. Če končno klavzula izstopi iz katerega koli od teh razlogov, ponovitev navodilo na koncu končno klavzula se nikoli ne izvrši. Zaradi ponovitev Navodilo ni zagotovljeno za izvedbo, nanj ni mogoče zanesti odstranitve povratnega naslova iz sklada. Zato je povratni naslov shranjen v lokalni spremenljivki na začetku datoteke končno miniaturna podprogram klavzule.

Za popoln primer si oglejte naslednjo metodo, ki vsebuje poskusite blok z dvema izstopnima točkama. V tem primeru sta obe izstopni točki vrnitev izjave:

 static int giveMeThatOldFashionedBoolean (logični bVal) {poskus {{if (bVal) {return 1; } vrni 0; } končno {System.out.println ("Staromodno."); }} 

Zgornja metoda se prevede v naslednje bajtode:

// Zaporedje bajt kod za poskusni blok: 0 iload_0 // Potisni lokalno spremenljivko 0 (arg je poslan kot delitelj) 1 ifeq 11 // Potisni lokalno spremenljivko 1 (arg je poslan kot dividenda) 4 iconst_1 // Push int 1 5 istore_3 // Pop an int (the 1), shrani v lokalno spremenljivko 3 6 jsr 24 // Skoči na mini podprogram za zadnji člen 9 iload_3 // Potisni lokalno spremenljivko 3 (the 1) 10 ireturn // Vrni int na vrh stack (the 1) 11 iconst_0 // Push int 0 12 istore_3 // Pop an int (the 0), shrani v lokalno spremenljivko 3 13 jsr 24 // Skoči na mini podprogram za končno klavzulo 16 iload_3 // Push local spremenljivka 3 (0) 17 ireturn // Vrne int na vrh sklada (0) // Zaporedje bajt kod za ulovno klavzulo, ki ujame kakršno koli izjemo //, vrženo iz bloka try. 18 astore_1 // Postavi sklic na vrženo izjemo, shrani // v lokalno spremenljivko 1 19 jsr 24 // Skoči na mini podprogram za končni člen 22 aload_1 // Potisni sklic (na vrženo izjemo) iz // lokalna spremenljivka 1 23 athrow // Vrni isto izjemo // Miniaturna podprogram, ki izvaja zaključni blok. 24 astore_2 // Pop povratni naslov, shranite ga v lokalno spremenljivko 2 25 getstatic # 8 // Pridobite sklic na java.lang.System.out 28 ldc # 1 // Potisnite iz konstantnega področja 30 invokevirtual # 7 // Pokliči System.out.println () 33 ret 2 // Vrnitev na povratni naslov, shranjen v lokalni spremenljivki 2 

Bytecode za poskusite blok vključuje dva jsr navodila. Še eno jsr Navodila so v ulov klavzulo. The ulov klavzulo doda prevajalnik, ker če se med izvajanjem datoteke vrže izjema poskusite blok, mora biti končni blok še vedno izveden. Zato je ulov klavzula se samo sklicuje na miniaturno podprogram, ki predstavlja končno , nato znova vrže isto izjemo. Tabela izjem za giveMeThatOldFashionedBoolean () Metoda, prikazana spodaj, kaže, da vsaka izjema, vržena med in vključno z naslovi 0 in 17 (vse bajtode, ki izvajajo poskusite blok) obravnava ulov klavzula, ki se začne na naslovu 18.

Tabela izjem: od do ciljne vrste 0 18 18 katera koli 

Bytecode datoteke končno klavzula se začne tako, da vrne naslov iz sklada in ga shrani v lokalno spremenljivko dva. Na koncu končno klavzula, ponovitev navodilo vzame svoj povratni naslov s pravega mesta, lokalna spremenljivka dva.

HopAround: simulacija navideznega stroja Java

Spodnji programček prikazuje navidezni stroj Java, ki izvaja zaporedje bajt kod. Zaporedje bajt kod v simulaciji je ustvarilo javac prevajalnik za hopAround () metoda spodaj prikazanega razreda:

razred Clown {static int hopAround () {int i = 0; while (true) {poskusite {poskusite {i = 1; } na koncu {// prva stavka na koncu i = 2; } i = 3; vrnitev i; // to se nikoli ne dokonča, ker je nadaljevanje} na koncu {// druga določba na koncu if (i == 3) {nadaljevanje; // to nadaljevanje preglasi povratni stavek}}}}} 

Bytecode, ki jih generira javac za hopAround () metode so prikazane spodaj:

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