Turbūt savaime suprantama, kad Java kompiliatorius neleistų kompiliuoti tokio kodo:
// DoubleReturnWrong.java
class DoubleReturnWrong {
static int method() {
return 1000;
return 2000;
}
public static void main(String ... args) {
System.out.println(method());
}
}
Kompiliatorius aptiks, kad 5-oje eilutėje esantis
return 2000; sakinys yra nepasiekiamas:
% javac DoubleReturnWrong
DoubleReturnWrong.java:5: unreachable statement
return 2000;
^
1 error
Abejonių tikriausiai nekels ir šis kodas:
// DoubleReturn1000
class DoubleReturn1000 {
static int method() {
boolean flag = true;
if (flag) return 1000;
return 2000;
}
public static void main(String ... args) {
System.out.println(method());
}
}
Kadangi metodas vykdomas iki pirmojo sutikto
return sakinio, ir po to priverstinai nutraukiamas, šiuo atveju
method() grąžins reikšmę 1000.
Dabar -- subtilesnė situacija:
// DoubleReturn.java
class DoubleReturn {
static int method() {
try {
return 1000;
} finally {
return 3000;
}
}
public static void main(String ... args) {
System.out.println(method());
}
}
1000 ar 3000? Pagal ankstesnį pavyzdį būtų logiška tikėtis, kad rezultatas -- 1000. T.y. grąžinama pirmo pasiekto
return reiškmė. Tačiau šiuo atveju antrasis
return yra
finally bloke, kuris privalo būti įvykdytas
bet kuriuo atveju, išskyrus System.exit() ar panašų visišką programos nutraukimą ankstesnėje try {...} arba catch(...) {...} dalyje, esančioje prieš finally bloką. Taigi, po
return 1000 vykdymas pratęsiamas
finally dalyje, kurioje tėra kitas
return sakinys su reikšme
3000. Kaip elgsis JVM šiuo atveju? Ar šiuo atveju bus kaip su
finalize() metodu (prisimenam, kad
finalize gali būti JVM
automatiškai įvykdytas
ne daugiau kaip vieną kartą. T.y. nėra ribojama, kiek kartų jis bus iškviestas iš kodo naudojant tiesioginį kvietinį, pvz.
myObject.finalize(), tačiau nepriklausomai nuo to JVM vis tiek
gali vieną kartą iškviesti šį metodą prieš objekto "sunaikinimą", bet ne daugiau. Su
finalize metodu yra niuansas -- metodas automatiškai iškviečiamas prieš objektą sunaikinant, tačiau šio metodo kodas gali sukurti "gyvą" nuorodą į objektą, ir taip apsaugoti jį nuo sunaikinimo. Tačiau tokiu atveju, kai objektas vėl pataps potencialiai naikinamu, JVM daugiau
finalize metodo jam neiškvietinės), t.y. JVM yra "pažymėjus", kad metode jau buvo įvykdytas
return sakinys?
Atsakymas -- ne. Rezultatas bus
3000. Kyla klausimas -- o kodėl? Patikrinkim "išdizassemblintą" kodą:
% javap DoubleReturn
Compiled from "DoubleReturn.java"
public class DoubleReturn extends java.lang.Object{
public DoubleReturn();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: return
static int method();
Code:
0: sipush 1000
3: istore_0
4: sipush 3000
7: ireturn
8: astore_1
9: sipush 3000
12: ireturn
Exception table:
from to target type
0 4 8 any
8 9 8 any
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #3; //Method method:()I
6: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
9: return
}
Kol kas nesu
Java bytecode specialistas, tačiau iš aukščiau pateikto
listingo aiškėja seka, kuria vykdomas metodas
method:
1. į steką įdedama 1000 (0-is metodo kodo adresas,
sipush 1000)
2. šokama į 4-ą adresą pagal
Exception Table (šioje vietoje
spėju, kad taip vyksta)
3. į steką įdedama 3000 (4-as kodo adresas,
sipush 3000)
4. grįžtama iš metodo su reikšme steke -- 3000 (7-ame kodo adrese esanti komanda
ireturn)
Taigi kaip matome iš sukompiliuoto
bytecode,
return reikšmė yra talpinama steke, ir paskutinė iš jų -- grąžinama.
Egzamine neteko matyti šį "kabliuką" turinčio klausimo, bet tai nereiškia, kad negali pakliūti kam nors kitam.