Czy ostatecznie blok zawsze jest wykonywany w Javie?

2382

Biorąc pod uwagę ten kod, czy mogę być absolutnie pewien, że finallyblok zawsze wykonuje się, bez względu na to, co something()jest?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}
Jonny Five
źródło
472
Jeśli nie, słowo kluczowe powinno zostać nazwane probably.
południe Silk
4
możliwy duplikat w Javie, czy w końcu zwraca atut?
polygenelubrykanty
37
Nie zawsze
Boann
2
Skuteczne java mówi inaczej informit.com/articles/article.aspx?p=1216151&seqNum=7
Binoy Babu
27
@BinoyBabu, finalizator ! = finally; finalizator == finalize()metoda.
jaco0646

Odpowiedzi:

2697

Tak, finallyzostanie wywołany po wykonaniu bloków trylub catchkodu.

Jedyne czasy, finallyktóre nie zostaną wywołane, to:

  1. Jeśli się odwołujesz System.exit()
  2. Jeśli się odwołujesz Runtime.getRuntime().halt(exitStatus)
  3. Jeśli JVM ulegnie awarii w pierwszej kolejności
  4. Jeśli JVM osiągnie nieskończoną pętlę (lub inną nieprzekraczalną, nie kończącą się instrukcję) w bloku trylubcatch
  5. Jeśli system operacyjny siłą zakończy proces JVM; np. kill -9 <pid>w systemie UNIX
  6. Jeśli system hosta umrze; np. awaria zasilania, błąd sprzętowy, panika systemu operacyjnego itp
  7. Jeśli finallyblok ma być wykonywany przez wątek demona, a wszystkie inne wątki inne niż demony wychodzą przed finallywywołaniem
jodonnell
źródło
44
W rzeczywistości thread.stop()niekoniecznie uniemożliwia wykonanie finallybloku.
Piotr Findeisen
181
Jak o powiedzieć, że finallyblok będzie wywoływana po tym trybloku i przed kontrola przechodzi się do następujących stwierdzeń. Jest to zgodne z blokiem try obejmującym nieskończoną pętlę, a zatem blok w końcu nigdy nie jest faktycznie wywoływany.
Andrzej Doyle,
9
jest też inny przypadek, gdy używamy zagnieżdżonych bloków try-catch-wreszcie
ruhungry
7
wreszcie blok nie jest wywoływany w przypadku wyjątku zgłoszonego przez wątek demona.
Amrish Pandey,
14
@BinoyBabu - Chodzi o finalizator, a nie w końcu blok
avmohan
567

Przykładowy kod:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

Wynik:

finally trumps return. 
0
Kevin
źródło
18
FYI: W języku C # zachowanie jest identyczne, z wyjątkiem faktu, że zamiana instrukcji w finallyklauzuli na- return 2;jest niedozwolona (błąd kompilatora).
Alexander Pacha
15
Oto ważny szczegół, o którym należy pamiętać: stackoverflow.com/a/20363941/2684342
WoodenKitty
18
Możesz nawet dodać instrukcję return w samym końcu bloku, który zastąpi poprzednią wartość zwracaną. To również magicznie odrzuca nieobsługiwane wyjątki. W tym momencie powinieneś rozważyć refaktoryzację kodu.
Zyl
8
To tak naprawdę nie dowodzi, że wreszcie atuty powracają. Wartość zwracana jest drukowana z kodu dzwoniącego. Wydaje się, że niewiele to dowodzi.
Trimtab
20
Przepraszam, ale to demonstracja, a nie dowód. Jest to tylko dowód na to, że możesz pokazać, że ten przykład zawsze zachowuje się w ten sposób na wszystkich platformach Java, I że podobne przykłady zawsze zachowują się w ten sposób.
Stephen C,
391

Ponadto, chociaż jest to zła praktyka, jeśli w ostatnim bloku znajduje się instrukcja return, przewyższy ona każdy inny zwrot ze zwykłego bloku. Oznacza to, że następujący blok zwróciłby wartość false:

try { return true; } finally { return false; }

To samo dotyczy rzucania wyjątków od bloku w końcu.

MooBob42
źródło
95
To NAPRAWDĘ zła praktyka. Zobacz stackoverflow.com/questions/48088/..., aby uzyskać więcej informacji o tym, dlaczego jest źle.
John Meagher,
23
Zgoda. Zwrot w końcu {} ignoruje każdy wyjątek zgłoszony podczas try {}. Straszny!
neu242,
8
@ dominicbri7 Dlaczego uważasz, że to lepsza praktyka? I dlaczego miałoby być inaczej, gdy funkcja / metoda jest nieważna?
corsiKa
8
Z tego samego powodu NIGDY nie używam goto w moich kodach C ++. Myślę, że wielokrotne zwroty utrudniają czytanie i utrudniają debugowanie (oczywiście w naprawdę prostych przypadkach nie ma to zastosowania). Sądzę, że to tylko osobiste upodobanie, a ostatecznie możesz osiągnąć to samo, stosując dowolną metodę
dominicbri7,
16
Zwykle używam wielu zwrotów, gdy zdarza się jakiś wyjątkowy przypadek. Na przykład, jeśli (istnieje powód, aby nie kontynuować) powrót;
iHearGeoff
257

Oto oficjalne słowa ze specyfikacji języka Java.

14.20.2. Wykonanie try-wreszcie i try-catch-wreszcie

trySprawozdanie z finallybloku jest wykonywany przez pierwsze wykonanie trybloku. Potem jest wybór:

  • Jeśli wykonanie trybloku zakończy się normalnie, [...]
  • Jeśli wykonanie trybloku zakończy się nagle z powodu throwwartości V , [...]
  • Jeśli wykonanie trybloku zakończy się nagle z jakiegokolwiek innego powodu R , wówczas finallyblok zostanie wykonany. Potem jest wybór:
    • Jeżeli wreszcie blokować normalnym zakończeniu, wówczas tryoświadczenie kończy się nagle z przyczyn R .
    • Jeśli finallyblok kończy się nagle z powodu S , wówczas tryinstrukcja kończy się nagle z powodu S ( i powód R jest odrzucany ).

Specyfikacja dla returntego wyraźnie wyjaśnia:

JLS 14.17 Deklaracja zwrotu

ReturnStatement:
     return Expression(opt) ;

returnOświadczenie bez Expression prób , aby przekazać kontrolę do wywołującego metody lub konstruktora, który go zawiera.

returnWraz z uzupełniającą Expression prób do przekazywania sterowania do wywołującego sposobu, który go zawiera; wartość parametru Expressionstaje się wartością wywołania metody.

Poprzednie opisy mówią „ próby przekazania kontroli ”, a nie tylko „ przekazywanie kontroli ”, ponieważ jeśli tryw metodzie lub konstruktorze są jakieś instrukcje, których trybloki zawierają returninstrukcję, wówczas wszelkie finallyklauzule tych tryinstrukcji zostaną wykonane w kolejności od najbardziej do najbardziej zewnętrznej , przed przekazaniem kontroli do wywołującego metodę lub konstruktora. Nagłe wypełnienie finallyklauzuli może zakłócić przekazanie kontroli zainicjowane przez returnoświadczenie.

środki smarujące wielotlenowe
źródło
163

Oprócz innych odpowiedzi ważne jest, aby zaznaczyć, że „w końcu” ma prawo nadpisać wszelkie wyjątki / zwrócone wartości przez blok try..catch. Na przykład następujący kod zwraca 12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

Podobnie poniższa metoda nie zgłasza wyjątku:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

Podczas gdy następująca metoda go wyrzuca:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}
Eyal Schneider
źródło
63
Należy zauważyć, że środkowy przypadek jest właśnie powodem, dla którego posiadanie instrukcji return w bloku wreszcie jest absolutnie okropne (może ukryć każdy Throwable).
Dimitris Andreou
2
Kto nie chce zaskoczonego OutOfMemoryError? ;)
RecursiveExceptionException
Przetestowałem to i eliminuje taki błąd (yipes!). Generuje również ostrzeżenie, kiedy je kompiluję (tak!). I można go obejść, definiując zmienną zwrotny, a następnie przy użyciu return retVal po tym finallybloku, mimo że oczywiście zakłada, że tłumione kilka innych wyjątków, ponieważ kod nie ma sensu inaczej.
Maarten Bodewes,
120

Próbowałem powyższy przykład z niewielką modyfikacją

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

Powyższy kod generuje:

wreszcie atuty wracają.
2)

Wynika to z tego, że kiedy return i;jest wykonywany, ima wartość 2. Po tym finallywykonywany jest blok, do którego przypisano 12, ia następnie System.outwykonywany jest out.

Po wykonaniu finallybloku tryblok zwraca 2 zamiast 12, ponieważ ta instrukcja return nie jest wykonywana ponownie.

Jeśli będzie debugowania tego kodu w Eclipse wtedy dostaniesz wrażenie, że po wykonaniu System.outz finallyblokowania returnoświadczenie trybloku jest ponownie wykonywane. Ale tak nie jest. Zwraca po prostu wartość 2.

wibhash
źródło
10
Ten przykład jest niesamowity, dodaje coś, o czym nie wspomniano w dziesiątkach powiązanych wątków. Myślę, że ledwie każdy programista to wie.
Mam nadzieję, że będzie
4
Co jeśli inie byłby prymitywnym, ale obiektem całkowitym.
Yamcha
Trudno mi zrozumieć tę sprawę. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 mówi: „Instrukcja return z wyrażeniem próbuje przenieść kontrolę do wywołującego metodę lub treść lambda, która zawiera it .... Jeśli ocena wyrażenia zakończy się normalnie, generując wartość V. ”Mogę zgadywać z tego stwierdzenia - wydaje się, że return nie ocenia wyrażenia ponownie, gdy ocenia wartość V, dlatego zmieniono nie wpływa na zwracaną wartość, popraw mnie.
meexplorer
Ale nie mogłem znaleźć żadnego dowodu na to, gdzie jest wspomniane, że return nie ocenia wyrażenia ponownie.
meexplorer
1
@meexplorer jest nieco spóźniony, ale wyjaśniono to w JLS 14.20.2. Wykonanie try-last i try-catch-wreszcie - brzmiało nieco skomplikowane, 14.17. Oświadczenie o zwrocie należy również przeczytać
85421
117

Oto opracowanie odpowiedzi Kevina . Ważne jest, aby wiedzieć, że wyrażenie, które ma zostać zwrócone, jest oceniane wcześniej finally, nawet jeśli jest zwracane później.

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

Wynik:

X
finally trumps return... sort of
0
WoodenKitty
źródło
8
Ważne, aby wiedzieć.
Aminadav Glickshtein
Dobrze wiedzieć, a także ma sens. Wygląda na to, że rzeczywiście zwraca się wartość zwrotu finally. Obliczanie wartości zwracanej ( printX()tutaj) jest jeszcze przed nią.
Albert
dobry przykład ze wszystkimi 3 „powracającymi” punktami!
radistao
Nie. Powyższy kod powinien zostać zastąpiony System.out.println("finally trumps return... sort of");przezSystem.out.print("finally trumps return in try"); return 42;
Pacerier
54

To cała idea ostatecznego bloku. Pozwala upewnić się, że robisz porządki, które w przeciwnym razie mogą zostać pominięte, ponieważ wracasz, między innymi, oczywiście.

W końcu zostaje wywołany niezależnie od tego, co dzieje się w bloku try ( chyba że zadzwonisz System.exit(int)lub maszyna wirtualna Java zostanie uruchomiona z innego powodu).

Chris Cooper
źródło
To bardzo słaba odpowiedź. stackoverflow.com/a/65049/715269
Gangnus
42

Logicznym sposobem myślenia na ten temat jest:

  1. Kod umieszczony w bloku ostatecznie musi zostać wykonany niezależnie od tego, co wystąpi w bloku try
  2. Jeśli więc kod w bloku try spróbuje zwrócić wartość lub zgłosić wyjątek, przedmiot jest umieszczany „na półce”, aż blok w końcu będzie mógł wykonać
  3. Ponieważ kod w ostatnim bloku ma (z definicji) wysoki priorytet, może zwrócić lub wyrzucić, co tylko zechce. W takim przypadku wszystko, co pozostało „na półce”, jest odrzucane.
  4. Jedynym wyjątkiem jest sytuacja, gdy maszyna wirtualna wyłącza się całkowicie podczas bloku try, np. Przez „System.exit”
Garth Gilmour
źródło
10
Czy to po prostu „logiczny sposób myślenia o tym”, czy też tak naprawdę powinien działać ostatecznie blok zgodnie ze specyfikacjami? Link do zasobu Sun byłby tutaj bardzo interesujący.
matias
21

ostatecznie jest zawsze wykonywane, chyba że wystąpi nieprawidłowe zakończenie programu (np. wywołanie System.exit (0) ..). więc twój sysout zostanie wydrukowany

shyam
źródło
18

Ostatecznie blok jest zawsze wykonywany, chyba że wystąpi nienormalne zakończenie programu, wynikające z awarii JVM lub wywołania System.exit(0).

Ponadto, każda wartość zwrócona z bloku ostatecznie zastąpi wartość zwróconą przed wykonaniem bloku ostatecznie, więc należy uważać na wszystkie punkty wyjścia podczas korzystania z try wreszcie.

użytkownik9189
źródło
18

Nie, nie zawsze jeden wyjątek to // System.exit (0); zanim blok w końcu uniemożliwia w końcu wykonanie.

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}
Rajendra Jadi
źródło
I to jest jeden z powodów, dla których naprawdę nigdy nie powinieneś wywoływać System.exit () ...
Franz D.
13

Wreszcie zawsze uruchamiane jest to o to chodzi, tylko dlatego, że pojawia się w kodzie po powrocie, nie oznacza, że ​​tak jest zaimplementowane. Środowisko wykonawcze Java jest odpowiedzialne za uruchomienie tego kodu podczas wychodzenia z trybloku.

Na przykład, jeśli masz następujące elementy:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

Środowisko wykonawcze wygeneruje coś takiego:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

Jeśli zgłoszony zostanie nieprzechwycony wyjątek, finallyblok zostanie uruchomiony, a wyjątek będzie kontynuowany.

Motti
źródło
11

Jest tak, ponieważ przypisałeś wartość i jako 12, ale nie zwróciłeś wartości i do funkcji. Prawidłowy kod jest następujący:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}
Wasim
źródło
10

Ponieważ ostatecznie blok będzie zawsze wywoływany, chyba że zadzwonisz System.exit()(lub wątek się zawiesi).

Jay Riggs
źródło
10

Krótko mówiąc, w oficjalnej dokumentacji Java (kliknij tutaj ) napisano, że -

Jeśli JVM zakończy działanie podczas wykonywania kodu try lub catch, blok w końcu może się nie uruchomić. Podobnie, jeśli wątek wykonujący kod try lub catch zostanie przerwany lub zabity, blok ostatecznie może nie zostać wykonany, nawet jeśli aplikacja jako całość będzie kontynuowana.

bikz05
źródło
10

Odpowiedź jest prosta TAK .

WEJŚCIE:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

WYNIK:

catch
finally
Spotykać się
źródło
1
Odpowiedź jest prosta NIE.
Christophe Roussy,
1
@ChristopheRoussy Jak? Czy możesz to wyjaśnić?
Spotkaj się
1
przeczytaj zaakceptowaną odpowiedź, pierwotne pytanie brzmi: „Czy zawsze będzie wykonywane” i nie zawsze. W twoim przypadku tak, ale to nie odpowiada na pierwotne pytanie, a nawet może wprowadzać w błąd początkujących.
Christophe Roussy
to w takim przypadku nie zostanie wykonane?
Spotkaj się
We wszystkich przypadkach wymienionych w innych odpowiedziach zobacz zaakceptowaną odpowiedź z ponad 1000 pozytywnych głosów.
Christophe Roussy,
9

Tak, zostanie wywołany. Taki jest sens posiadania wreszcie słowa kluczowego. Jeśli wyskakiwanie z bloku try / catch może po prostu pominąć blok ostatecznie, to to samo, co umieszczenie pliku System.out.println poza try / catch.

Mendelt
źródło
9

Tak, w końcu blok jest zawsze wykonywany. Większość programistów używa tego bloku do zamykania połączenia z bazą danych, obiektu wynikowego, obiektu instrukcji, a także używa hibernacji java do wycofania transakcji.

Gautam Viradiya
źródło
9

NIE ZAWSZE

Specyfikacja języka Java opisuje sposób try- catch- finallyi try- catchbloki pracują w 14.20.2
W żadnym miejscu nie określa, że finallyblok jest zawsze wykonywany. Jednak we wszystkich przypadkach, w których try- catch- finallyi try- finallybloki zakończyć to jednak określić, że przed zakończeniem finallymuszą być wykonywane.

try {
  CODE inside the try block
}
finally {
  FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).

JLS nie gwarantuje wykonania FIN po kodzie . JLS gwarantuje, że jeśli zostaną wykonane KOD i NASTĘPNY, to FIN będzie zawsze wykonywany po KOD i przed NASTĘPNYM .

Dlaczego JLS nie gwarantuje, że finallyblok jest zawsze wykonywany po trybloku? Ponieważ to niemożliwe. Jest mało prawdopodobne, ale możliwe, że JVM zostanie przerwana (zabijanie, awaria, wyłączenie zasilania) tuż po ukończeniu trybloku, ale przed wykonaniem finallybloku. JLS nie może nic zrobić, aby tego uniknąć.

Zatem każde oprogramowanie, które dla ich prawidłowego zachowania zależy od finallybloków zawsze wykonywanych po ich tryukończeniu, jest błędne.

returninstrukcje w trybloku nie mają znaczenia dla tego problemu. Jeśli wykonanie osiągnie kod po try- catch- finallyjest zagwarantowane, że finallyblok zostanie wykonany przed returninstrukcją wewnątrz trybloku lub bez niego .

Anonimowy Tchórz
źródło
8

Tak, to będzie. Bez względu na to, co się stanie w bloku try lub catch, chyba że wywołano System.exit () lub JVM uległo awarii. jeśli w bloku (blokach) znajduje się jakaś instrukcja return, ostatecznie zostanie wykonana przed tą instrukcją return.

Karthikeyan
źródło
8

Tak, to będzie. Tylko, że tak nie będzie, JVM kończy działanie lub ulega awarii

abhig
źródło
8

Dodanie do odpowiedzi @ vibhash, ponieważ żadna inna odpowiedź nie wyjaśnia, co dzieje się w przypadku obiektu zmiennego, takiego jak ten poniżej.

public static void main(String[] args) {
    System.out.println(test().toString());
}

public static StringBuffer test() {
    StringBuffer s = new StringBuffer();
    try {
        s.append("sb");
        return s;
    } finally {
        s.append("updated ");
    }
}

Wyjdzie

sbupdated 
Pradeep Kumaresan
źródło
Od wersji Java 1.8.162 nie jest to wynik.
Sam
8

Próbowałem tego, jest to jednowątkowy.

public static void main(String args[]) throws Exception {
    Object obj = new Object();
    try {
        synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
        }
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

main ThreadBędzie na waitstanie na zawsze, a więc finallynigdy nie zostanie wywołana ,

więc wyjście konsoli nie będzie print String: po wait()lubfinally

Powyższy przykład, uzgodniony z @Stephen C, jest jedną z 3. wzmianek o przypadkach tutaj :

Dodanie kilku takich możliwości nieskończonej pętli w następującym kodzie:

// import java.util.concurrent.Semaphore;

public static void main(String[] args) {
    try {
        // Thread.sleep(Long.MAX_VALUE);
        // Thread.currentThread().join();
        // new Semaphore(0).acquire();
        // while (true){}
        System.out.println("after sleep join semaphore exit infinite while loop");
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

Przypadek 2: Jeśli JVM ulegnie awarii w pierwszej kolejności

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public static void main(String args[]) {
    try {
        unsafeMethod();
        //Runtime.getRuntime().halt(123);
        System.out.println("After Jvm Crash!");
    } catch (Exception e) {
    } finally {
        System.out.println("finally");
    }
}

private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    unsafe.putAddress(0, 0);
}

Ref: W jaki sposób zawiesisz JVM?

Przypadek 6: Jeśli finallyblok ma być wykonywany przez demona Threadi wszystkie inne Threadswyjścia inne niż demony przed finallywywołaniem.

public static void main(String args[]) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                printThreads("Daemon Thread printing");
                // just to ensure this thread will live longer than main thread
                Thread.sleep(10000);
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }
    };
    Thread daemonThread = new Thread(runnable);
    daemonThread.setDaemon(Boolean.TRUE);
    daemonThread.setName("My Daemon Thread");
    daemonThread.start();
    printThreads("main Thread Printing");
}

private static synchronized void printThreads(String str) {
    System.out.println(str);
    int threadCount = 0;
    Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
    for (Thread t : threadSet) {
        if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
            System.out.println("Thread :" + t + ":" + "state:" + t.getState());
            ++threadCount;
        }
    }
    System.out.println("Thread count started by Main thread:" + threadCount);
    System.out.println("-------------------------------------------------");
}

wyjście: To nie drukuje „w końcu”, co sugeruje, że „W końcu blok” w „wątku demona” nie został wykonany

main Thread Printing  
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED  
Thread :Thread[main,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE   
Thread count started by Main thread:3  
-------------------------------------------------  
Daemon Thread printing  
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE  
Thread count started by Main thread:2  
-------------------------------------------------  

Process finished with exit code 0
dkb
źródło
4
Zobacz zaakceptowaną odpowiedź. To tylko skrajny przypadek „nieskończonej pętli”.
Stephen C,
8

Rozważ następujący program:

public class SomeTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
        System.out.println("---PRINT THE RESULT---");
        System.out.println(sb.toString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

Od wersji Java 1.8.162 powyższy blok kodu daje następujące dane wyjściowe:

-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz

oznacza to, że używanie finallydo zwalniania obiektów jest dobrą praktyką, taką jak następujący kod:

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null; // Just an example, but you can close streams or DB connections this way.
    }
}
Sam
źródło
Czy nie powinno być sb.setLength(0)wreszcie?
user7294900
sb.setLength (0) po prostu opróżni dane w StringBuffer. Zatem sb = null oddzieli obiekt od odwołania.
Sam
Czy „xyz” nie powinien być drukowany dwukrotnie na wydruku? Ponieważ funkcja została wywołana dwukrotnie, dlaczego „w końcu” było tylko raz?
fresko
2
To nie jest dobra praktyka. Ten blok w końcu sb = null;dodaje niepotrzebny kod. Rozumiem, że masz na myśli, że finallyblok jest dobrym miejscem do zwolnienia zasobów, takich jak połączenie z bazą danych lub coś takiego, ale pamiętaj, że twój przykład może wprowadzać zamieszanie w nowych.
Roc Boronat
1
@Simim Dzięki, dodałem linie System.out.println("---AGAIN2---"); System.out.println(sb);i jest teraz bardziej przejrzyste. W tej chwili wynik był niezgodny z twoją tezą: p Dodałem również do twojej odpowiedzi, ale zmiana musi zostać zaakceptowana przez moderatora lub kogoś takiego. W przeciwnym razie możesz je dodać
fresko
7

Tak naprawdę jest to prawdą w każdym języku ... w końcu zawsze będzie wykonywane przed instrukcją return, bez względu na to, gdzie zwrot ten znajduje się w treści metody. Gdyby tak nie było, blok w końcu nie miałby większego znaczenia.

Scott Dorman
źródło
7

Oprócz kwestii powrotu do ostatecznego zastąpienia zwrotu w bloku try, to samo dotyczy wyjątku. Wreszcie blok, który zgłasza wyjątek, zastąpi zwrot lub wyjątek zgłoszony z bloku try.

Alex Miller
źródło
7

finally wykona się i to na pewno.

finally nie będzie działać w poniższych przypadkach:

przypadek 1 :

Podczas wykonywania System.exit().

przypadek 2:

Gdy następuje awaria JVM / wątku.

przypadek 3:

Gdy wykonanie zostanie zatrzymane pomiędzy nimi ręcznie.

Utkash Bhatt
źródło
6
  1. W końcu Blok zawsze jest wykonywany. Chyba że i do System.exit () nie istnieje instrukcja (pierwsza instrukcja w końcu blok).
  2. Jeśli system.exit () jest pierwszą instrukcją, to w końcu blok nie zostanie wykonany, a kontrola wyjdzie z bloku w końcu. Ilekroć instrukcja System.exit () dostaje się w końcu do bloku, aż ta instrukcja w końcu zostanie wykonana i kiedy pojawi się System.exit (), wtedy siła sterująca w pełni wychodzi z bloku w końcu.
Avinash Pande
źródło
1
Odpowiedzi udzielono wiele razy, więc jakie nowe informacje dodaje twoja odpowiedź?
Tom