Pytanie Czy w końcu zawsze wykonuje się w Javie?


Biorąc pod uwagę ten kod, mogę być absolutnie pewny że finally blok zawsze wykonuje, bez względu na wszystko 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.");
}

1907
2017-09-15 17:43


pochodzenie


Nawet po przetestowaniu tego samodzielnie, wiesz tylko co Twój implementacja lub wersja Java / JVM to zrobi i nie będzie wiedziała co kod powinien zrobić (zgodnie ze standardem), a więc pytanie pozostaje ważne. Cieszę się, że te aspekty znalazły się wśród różnych odpowiedzi. - Rhubbarb
Nie zawsze - Boann
Skuteczne java mówi inaczej informit.com/articles/article.aspx?p=1216151&seqNum=7 - Binoy Babu
@BinoyBabu, finalizer ! = finally; finalizer == the finalize() metoda. - jaco0646
@LordFarquaad ;-) To jest rzeczywiście zawsze gwarantowane. - MC Emperor


Odpowiedzi:


Tak, finally zostanie wywołany po wykonaniu bloków try lub catch code.

Tylko raz finally nie będą wywoływane:

  1. Jeśli się powołujesz System.exit();
  2. Jeśli JVM najpierw ulegnie awarii;
  3. Jeśli maszyna JVM osiągnie nieskończoną pętlę (lub inną nieprzewidywalną, nie kończącą się instrukcję) w try lub catch blok;
  4. Jeśli system operacyjny przymusowo zakończy proces JVM; na przykład "kill -9" w systemie UNIX.
  5. Jeśli umiera system hosta; na przykład awaria zasilania, błąd sprzętu, panik systemu operacyjnego itp.
  6. Jeśli w końcu blok zostanie wykonany przez wątek demona i wszystkie inne wątki nie będące demonami zostaną zakończone, zanim zostanie wywołany.

2122
2017-09-15 17:45



Tak właściwie thread.stop() niekoniecznie zapobiega finally blokowanie od wykonania. - Piotr Findeisen
Co powiesz na to, że finally blok zostanie wywołany po  try blok, i przed kontrola przechodzi do następujących instrukcji. Jest to zgodne z blokiem try obejmującym nieskończoną pętlę, a zatem blok finally nigdy nie jest wywoływany. - Andrzej Doyle
jest też inny przypadek, kiedy używamy zagnieżdżonego try-catch-finally Bloki - ruhungry
także, w końcu blok nie jest wywoływany w przypadku wyjątku zgłaszanego przez wątek demona. - Amrish Pandey
@BinoyBabu - To o finalizatorze, a nie w końcu bloku - avmohan


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.");
    }
}

Wydajność:

finally trumps return. 
0

446
2017-09-15 17:59



FYI: W C # zachowanie jest identyczne oprócz tego, że zastępowanie instrukcji w finally-clause z return 2; jest niedozwolone (błąd kompilatora). - Alexander Pacha
Oto ważny szczegół, o którym należy pamiętać: stackoverflow.com/a/20363941/2684342 - WoodenKitty
Możesz nawet dodać instrukcję return w samym bloku finally, co spowoduje nadpisanie poprzedniej wartości zwracanej. To również magicznie odrzuca nieobsługiwane wyjątki. W tym momencie powinieneś rozważyć refaktoryzację swojego kodu. - Zyl
To naprawdę nie dowodzi, że w końcu powracają atuty. Wartość zwracana jest drukowana z kodu wywołującego. Wygląda na to, że niewiele dowodzi. - Trimtab
Przepraszam, ale to jest demonstracja, a nie dowód. Jest to tylko dowód, jeśli można pokazać, że ten przykład zawsze zachowuje się w ten sposób na wszystkich platformach Java, i że podobne przykłady zachowują się zawsze w ten sposób. - Stephen C


Ponadto, pomimo tego, że jest to zła praktyka, jeśli w bloku finally znajduje się instrukcja return, to spowoduje to przywrócenie dowolnego innego zwrotu z regularnego bloku. Oznacza to, że poniższy blok zwróci false:

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

To samo z rzucaniem wyjątków z bloku finally.


325
2017-09-15 18:19



Jest to NAPRAWDĘ zła praktyka. Widzieć stackoverflow.com/questions/48088/... Więcej informacji o tym, dlaczego jest zły. - John Meagher
Zgoda. Zwrot w końcu {} ignoruje każdy wyjątek zgłoszony w try {}. Straszny! - neu242
@ Dominicbri7 Dlaczego uważasz, że to lepsza praktyka? I dlaczego powinno być inaczej, gdy funkcja / metoda jest nieważna? - corsiKa
Z tego samego powodu NIGDY nie używam goto w moich kodach C ++. Myślę, że wiele zwrotów utrudnia czytanie i trudniejsze do debugowania (oczywiście w naprawdę prostych przypadkach nie ma zastosowania). Sądzę, że jest to po prostu personalna preferencja i na końcu możesz osiągnąć to samo przy użyciu dowolnej z tych metod - dominicbri7
Zwykle używam wielu zwrotów, gdy dzieje się coś wyjątkowego. Tak jakby (powód nie do kontynuowania) powrócił; - iHearGeoff


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

14.20.2. Realizacja try-finally i try-catch-finally

ZA try oświadczenie z finally blok jest wykonywany przez pierwsze wykonanie try blok. Następnie jest wybór:

  • Jeśli wykonanie try blok kończy się normalnie, [...]
  • Jeśli wykonanie try blok kończy się nagle z powodu a throw wartości V, [...]
  • Jeśli wykonanie try blok kończy się nagle z jakiegokolwiek innego powodu R, a później finally blok jest wykonywany. Następnie jest wybór:
    • Jeśli blok finally kończy się normalnie, to try oświadczenie kończy się nagle z powodu R.
    • Jeśli finally blok kończy się nagle z powodu S, a później try oświadczenie kończy się nagle z powodu S (i powód R jest odrzucany).

Specyfikacja dla return w rzeczywistości czyni to jawnie:

JLS 14.17 Deklaracja zwrotu

ReturnStatement:
     return Expression(opt) ;

ZA return oświadczenie z nr Expression  próbowanie aby przekazać sterowanie do wywołującego metody lub konstruktora, który je zawiera.

ZA return oświadczenie z Expression  próbowanie przekazać kontrolerowi wywołującemu metodę, która go zawiera; wartość Expression staje się wartością wywołania metody.

Poprzednie opisy mówią "próbowanie przekazać kontrolę"raczej niż"kontrola transferów"ponieważ jeśli są jakieś try instrukcje w ramach metody lub konstruktora, którego try bloki zawierają return oświadczenie, a następnie dowolne finally klauzule tych try instrukcje będą wykonywane w kolejności od najbardziej do najmniejszej, zanim kontrola zostanie przekazana do wywołującego metody lub konstruktora. Nagłe zakończenie a finally Klauzula może zakłócić przekazanie kontroli zainicjowanej przez return komunikat.


229
2018-05-25 06:50





Oprócz innych odpowiedzi, ważne jest, aby zaznaczyć, że "w końcu" ma prawo do przesłonięcia każdego wyjątku / zwróconej wartości przez blok try..catch. Na przykład poniższy kod zwraca 12:

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

Podobnie poniższa metoda nie rzuca wyjątku:

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

Podczas poniższej metody:

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

134
2018-05-13 07:11



Należy zauważyć, że środkowy przypadek jest właśnie powodem, dla którego posiadanie zwrotnego oświadczenia wewnątrz bloku końcowego jest absolutnie okropne (mogłoby to ukryć jakiekolwiek Throwable). - Dimitris Andreou


Próbowałem powyższego przykładu z niewielkimi modyfikacjami-

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 wyjściowy:

wreszcie atuty wracają.
  2

To dlatego, że kiedy return i; jest wykonywany i ma wartość 2. Następnie finally blok jest wykonywany, gdy 12 jest przypisane do i i wtedy System.out out jest wykonywane.

Po wykonaniu finally zablokować try block zwraca 2, zamiast zwracać 12, ponieważ ta instrukcja return nie jest ponownie wykonywana.

Jeśli będziesz debugować ten kod w Eclipse, poczujesz, że po uruchomieniu System.out z finally zablokować return oświadczenie o try blok jest ponownie wykonywany. Ale tak nie jest. Po prostu zwraca wartość 2.


88
2017-11-17 16:25



Ten przykład jest niesamowity, dodaje coś, czego nie wymieniono w dziesiątkach ostatecznie powiązanych wątków. Myślę, że prawie każdy deweloper to wie. - HopefullyHelpful
Co jeśli i nie był obiektem pierwotnym, lecz obiektem Integer. - Yamcha
Trudno mi zrozumieć tę sprawę. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 mówi, że "Instrukcja return z wyrażeniem próbuje przekazać kontrolę inwokrowi metody lub treści lambda, która go zawiera ... Jeśli ocena wyrażenia kończy się normalnie, tworząc wartość V .." Co mogę odgadnąć z to stwierdzenie jest - wydaje się, że return nie ocenia wyrażenia ponownie, gdy ocenia wartość V, dlatego zmieniono i nie wpływa na zwracaną wartość, popraw mnie. - meexplorer
Ale nie znalazłem żadnego dowodu na to, gdzie jest wspomniany, że powrót nie ocenia ponownie wyrażenia. - meexplorer
@meexplorer trochę późno, ale jest wyjaśnione w JLS 14.20.2. Realizacja try-finally i try-catch-finally - sformułowane nieco skomplikowane, 14,17. Deklaracja zwrotu należy również przeczytać - Carlos Heuberger


Oto opracowanie Odpowiedź Kevina. Ważne jest, aby wiedzieć, że wyrażenie, które ma zostać zwrócone, jest wcześniej oceniane finally, nawet jeśli zostanie zwrócony 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");
    }
}

Wydajność:

X
finally trumps return... sort of
0

80
2017-12-03 23:36



Ważne wiedzieć. - Aminadav Glickshtein


Taka jest idea bloku w końcu. To pozwala upewnić się, że czyścisz, które w przeciwnym razie można pominąć, ponieważ między innymi powrócisz.

W końcu zostaje wywołany niezależnie od tego, co się stanie w bloku try (chyba że ty dzwonisz System.exit(int) lub maszyna wirtualna Java jest uruchamiana z innego powodu).


46
2018-05-13 06:19





Logicznym sposobem na przemyślenie tego jest:

  1. Kod umieszczony w bloku finally musi zostać wykonany cokolwiek się zdarzy w bloku try
  2. Więc jeśli kod w bloku try spróbuje zwrócić wartość lub wyrzucić wyjątek, pozycja jest umieszczana "na półce", aż blok finally będzie mógł zostać wykonany
  3. Ponieważ kod w bloku finally ma (z definicji) wysoki priorytet, może on zwracać lub rzucać, co tylko chce. W takim przypadku wszystko, co zostało "na półce", zostaje odrzucone.
  4. Jedynym wyjątkiem od tej reguły jest to, że maszyna wirtualna zostaje całkowicie wyłączona podczas blokady try, np. przez "System.exit"

31
2017-09-15 19:26



Czy to tylko "logiczny sposób na przemyślenie tego", czy tak naprawdę blok docelowy ma działać zgodnie ze specyfikacjami? Link do zasobu Słońca byłby tutaj bardzo interesujący. - matias
Punkt 4 jest sprzeczny z whatever happens oświadczenie w punkcie 1 - ojonugwa ochalifu


Również zwrot w końcu wyrzuci każdy wyjątek. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


14
2017-09-15 19:26