Mam prostą klasę Java, jak pokazano poniżej:
public class Test {
private String s;
public String foo() {
try {
s = "dev";
return s;
}
finally {
s = "override variable s";
System.out.println("Entry in finally Block");
}
}
public static void main(String[] xyz) {
Test obj = new Test();
System.out.println(obj.foo());
}
}
Wynik tego kodu jest następujący:
Entry in finally Block
dev
Dlaczego s
nie jest nadpisywany w finally
bloku, ale kontroluje wydruk?
java
try-finally
Dev
źródło
źródło
s
zanim zmienisz jego wartość.finally
bloku , w przeciwieństwie do C # (co nie można)Odpowiedzi:
W
try
finalizuje bloku z realizacjąreturn
oświadczenia i wartościs
w momenciereturn
oświadczenie wykonuje to wartość zwracana przez metodę. Fakt, żefinally
klauzula później zmienia wartośćs
(po zakończeniureturn
instrukcji) nie zmienia (w tym momencie) wartości zwracanej.Zwróć uwagę, że powyższe dotyczy zmian wartości
s
samego siebie wfinally
bloku, a nie obiektu, do którego sięs
odnosi. Gdybys
było odniesieniem do zmiennego obiektu (aString
nie jest), a zawartość obiektu została zmieniona wfinally
bloku, to te zmiany byłyby widoczne w zwracanej wartości.Szczegółowe zasady działania tego wszystkiego można znaleźć w sekcji 14.20.2 specyfikacji języka Java . Należy zauważyć, że wykonanie
return
instrukcji liczy się jako nagłe zakończenietry
bloku (obowiązuje sekcja rozpoczynająca się od „ Jeśli wykonanie bloku try nagle kończy się z innego powodu R… ”). Zobacz sekcję 14.17 JLS, aby dowiedzieć się, dlaczegoreturn
oświadczenie jest nagłym zakończeniem bloku.W celu uzyskania dalszych szczegółów: jeśli zarówno
try
blokada, jak ifinally
blokadatry-finally
oświadczeniareturn
ulegają nagłemu przerwaniu z powodu oświadczeń, wówczas obowiązują następujące zasady z §14.20.2:W rezultacie
return
instrukcja wfinally
bloku określa zwracaną wartość całejtry-finally
instrukcji, a wartość zwracana ztry
bloku jest odrzucana. Podobnie dzieje się wtry-catch-finally
instrukcji, jeślitry
blok zgłasza wyjątek, zostaje przechwycony przezcatch
blok i zarówno blok, jakcatch
ifinally
blok mająreturn
instrukcje.źródło
finally
blok nie zmienia zwracanego obiektu (theStringBuilder
), ale może zmienić elementy wewnętrzne obiektu. Wfinally
Wykonuje bloku przed metody faktycznie zwraca (mimo żereturn
oświadczenie zostało zakończone), więc te zmiany nastąpić przed wywołaniem kodu widzi zwracanej wartości.StringBuilder
,List
,Set
, reklama nauseum): jeśli zmienisz zawartość wfinally
bloku, to te zmiany są widoczne w wywołującego kodu gdy metoda wreszcie wyjść.Ponieważ wartość zwracana jest umieszczana na stosie przed wywołaniem last.
źródło
=
byłby zmienny, użycie nie spowodowałoby jego mutacji.finally
blok OP nie wpływa na zwracaną wartość. Myślę, że to, do czego mógł dojść z templatetypedef (chociaż nie jest to jasne), polega na tym, że ponieważ zwracana wartość jest odniesieniem do niezmiennego obiektu, nawet zmiana kodu wfinally
bloku (inna niż użycie innejreturn
instrukcji) nie może wpłynąć na wartość zwracana z metody.Jeśli zajrzymy do kodu bajtowego, zauważymy, że JDK dokonał znaczącej optymalizacji, a metoda foo () wygląda następująco:
I kod bajtowy:
java zachował ciąg znaków „dev” przed zmianą przed zwróceniem. W rzeczywistości nie ma tu żadnego ostatecznego bloku.
źródło
Na uwagę zasługują 2 rzeczy:
źródło
finally
klauzuli, byłoby to widoczne w kodzie wywołującym. Jeśli jednak przypiszesz nowy bufor łańcuchowy dos
, zachowanie będzie takie samo jak teraz.Zmieniam trochę twój kod, żeby udowodnić cel Teda.
Jak widać na wyjściu
s
jest rzeczywiście zmieniony ale po powrocie.Wynik:
źródło
Technicznie rzecz biorąc,
return
blok w try nie będzie ignorowany, jeślifinally
blok jest zdefiniowany, tylko jeśli ten ostatni blok zawiera równieżreturn
.Jest to wątpliwa decyzja projektowa, która prawdopodobnie była błędem z perspektywy czasu (podobnie jak odniesienia domyślnie dopuszczają wartość null / mutable i, według niektórych, sprawdzane wyjątki). Pod wieloma względami to zachowanie jest dokładnie zgodne z potocznym rozumieniem tego, co
finally
oznacza - „bez względu na to, co wydarzy się wcześniej wtry
bloku, zawsze uruchamiaj ten kod”. Dlatego jeśli zwrócisz prawdę zfinally
bloku, ogólny efekt zawsze musi być doreturn s
, nie?Ogólnie rzecz biorąc, rzadko jest to dobry idiom i powinieneś
finally
swobodnie używać bloków do czyszczenia / zamykania zasobów, ale rzadko, jeśli w ogóle, zwracaj z nich wartość.źródło
Spróbuj tego: Jeśli chcesz wydrukować wartość przesłonięcia s.
źródło