Питання Нарешті завжди виконується в Java?


Зважаючи на цей код, чи можу я бути абсолютно впевнений що finally блок завжди виконує, незалежно від того, що something() є

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


походження


Навіть після того, як це випробувати себе, ви тільки знаєте що твій реалізація або версія Java / JVM зробить, і не будемо знати, який код повинен робити (відповідно до стандарту), і тому питання залишається дійсним. Я радий, що ці аспекти розглядаються серед різних відповідей. - Rhubbarb
Ні завжди - Boann
Ефективна java говорить інакше informit.com/articles/article.aspx?p=1216151&seqNum=7 - Binoy Babu
@ BinoyBabu фіналіст ! = finally; фіналіст == the finalize() метод - jaco0646
@LordFarquaad ;-) Це дійсно завжди гарантовано. - MC Emperor


Відповіді:


так, finally буде викликатися після виконання блоків спробувати або зловити код.

Єдиний раз finally не будуть називатися:

  1. Якщо ви закликаєте System.exit();
  2. Якщо JVM перший випаде;
  3. Якщо JVM досягає нескінченного циклу (або іншого неперерваного, незакінченого виразу) у try або catch блок;
  4. Якщо ОС насильно припиняє процес JVM; наприклад, "kill -9" на UNIX.
  5. Якщо хост система помирає; наприклад, несправність живлення, апаратна помилка, паніка операційної системи тощо.
  6. Якщо, нарешті, блок буде виконуватися за допомогою потоку daemon, а всі інші потоки, крім демона, поки не називаються.

2122
2017-09-15 17:45



Насправді thread.stop() не обов'язково запобігає finally блокувати від виконання. - Piotr Findeisen
Як нас говорять, що finallyблок буде викликаний після в try блок, і раніше Контроль переходить до наступних тверджень. Це узгоджується з блоком try з нескінченним циклом і, отже, остаточно блоком, який ніколи не називається. - Andrzej Doyle
є ще один випадок, коли ми використовуємо вкладені спробуй ловити-нарешті блоки - ruhungry
також, нарешті, блок не викликається у випадку винятку, викинутого потоком daemon. - Amrish Pandey
@BinoyBabu - Це про фіналістку, а не нарешті заблокувати - avmohan


Приклад коду:

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

Вихід:

finally trumps return. 
0

446
2017-09-15 17:59



FYI: У C # поведінка є ідентичною, крім того, що замінюючи оператор в finally-промовте з return 2; не дозволено (Компілятор-помилка). - Alexander Pacha
Тут важлива деталь, щоб бути в курсі: stackoverflow.com/a/20363941/2684342 - WoodenKitty
Ви навіть можете додати оператор return у самому кінцевому блоці, який потім перевизначить попереднє значення повернення. Це також чарівно відкидає невикористані винятки. На цьому етапі слід розглянути можливість рефакціонування вашого коду. - Zyl
Це насправді не доводить, що нарешті козирі повертаються. Повертає значення друкується з коду абонента. Здається, не довелося багато чого. - Trimtab
Вибачте, але це демонстрація не доказ. Це лише доказ, якщо ви можете показати, що цей приклад завжди поводиться таким чином на всіх платформах Java, і що подібні приклади завжди завжди поводяться таким чином. - Stephen C


Крім того, хоча це погана практика, якщо в остаточному блоці є зворотній вираз, він буде супроводжувати будь-який інший повернення з регулярного блоку. Тобто, наступний блок буде повертати помилково:

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

Те ж саме з викидами з остаточного блоку.


325
2017-09-15 18:19



Це дійсно погана практика. Побачити stackoverflow.com/questions/48088/... для отримання додаткової інформації про те, чому це погано. - John Meagher
Згоден Повернення всередині нарешті {} ігнорує будь-яке виняток, викинутий у try {}. Страшно! - neu242
@ dominicbri7 Чому ви вважаєте це кращою практикою? І чому це повинно бути іншим, коли функція / метод недійсний? - corsiKa
З цієї ж причини я НІКОЛИ не використовую goto в моїх кодах C ++. Я думаю, що кілька повернень ускладнює читання та ускладнює налагодження (звичайно, у дійсно простих випадках це не застосовується). Я думаю, це лише особиста перевага, і в підсумку ви можете досягти однієї і тієї ж якості, використовуючи той чи інший метод - dominicbri7
Я, як правило, використовую ряд повернень, коли виникає якийсь винятковий випадок. Як і якщо (є причина не продовжувати) повертатися; - iHearGeoff


Ось офіційні слова з специфікації мови Java.

14.20.2. Виконання спроби-нарешті і спроба-ловити-нарешті

А. try заява з finally блок виконується першим виконанням try блок Тоді є вибір:

  • Якщо виконання try блок завершується нормально, [...]
  • Якщо виконання try блок швидко закінчується через a throw від вартості V, [...]
  • Якщо виконання try блок завершується різкими зусиллями з будь-якої іншої причини Р., то в finally блок виконаний. Тоді є вибір:
    • Якщо остаточно завершений блок, як правило, то try Заява завершується різко за причиною Р..
    • Якщо finally блок назавжди закінчується причиною S, то в try Заява завершується різко за причиною S (і причина Р. відкидається)

Специфікація для return насправді робить це явним:

JLS 14.17 Повернення декларації

ReturnStatement:
     return Expression(opt) ;

А. return заява з ні Expression  спроби для передачі керування виклику методу або конструктора, який містить його.

А. return заява з Expression  спроби для передачі керування виклику методу, який його містить; вартість Expression стає значенням виклику методу.

Попередні описи говорять "спроби для передачі контролю"а не просто"передає контроль"тому що якщо є якісь try оператори в межах методу або конструктора, чиї try блоки містять return вислів, потім будь-який finally положення цих try висловлювання будуть виконуватися, в порядку, найбідніших до найвіддаленіших, перш ніж управління буде передано виклику методу або конструктору. Різке завершення а finally Застереження може порушити передачу контролю, ініційованого a return заява


229
2018-05-25 06:50





Окрім інших відповідей, важливо зазначити, що "нарешті" має право відмінити будь-яке виняток / повернене значення блоком try..catch. Наприклад, наступний код повертає 12:

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

Аналогічним чином, наступний метод не викидає виняток:

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

Хоча наступний спосіб кидає це:

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

134
2018-05-13 07:11



Слід зазначити, що середній випадок - це саме те причина, через яку повернення в кінцевий блок абсолютно жахливо (це може приховати будь-який Throwable). - Dimitris Andreou


Я спробував наведений вище приклад з невеликою зміною -

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

Викладені вище кодові виходи:

нарешті, козир повертається.
  2

Це тому, коли return i; виконується i має значення 2. Після цього finally блок виконується, де 12 присвоєно i і потім System.out виконується.

Після виконання finally заблокувати try блок повертає 2, а не повертає 12, тому що це оператор повернення не виконується знову.

Якщо ви будете налагоджувати цей код в Eclipse, ви отримаєте відчуття, що після виконання System.out від finally заблокувати return заява про try блок виконується знову. Але це не так. Це просто повертає значення 2.


88
2017-11-17 16:25



Цей приклад чудовий, він додає те, що не було згадано в десятках остаточно відповідних потоків. Я думаю, що майже ніхто з розробників це не знає. - HopefullyHelpful
А якщо i був не примітивним, а цілим об'єктом. - Yamcha
Мені важко зрозуміти цю справу. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 каже, що "оператор повернення з виразом намагається передавати керування виклику методу або тіла лямбда, яке містить його .... Якщо оцінка Expression виконується нормально, виробляючи значення V .." Що я міг здогадатися з це твердження - здається, що повернення не оцінює вираз ще раз, коли він оцінює значення V, тому змінений я не впливає на повернене значення, виправити мене. - meexplorer
Але я не знайшов жодних доказів щодо цього, де згадується, що повернення не оцінює вираз ще раз. - meexplorer
@ meexplorer трохи пізно, але це пояснюється в JLS 14.20.2. Виконання спроби-нарешті і спроба-ловити-нарешті - сформулювали трохи складніше, 14.17. Повернення Заява також слід читати - Carlos Heuberger


Ось підсумки Відповідь Кевіна. Важливо знати, що вираження, яке буде повернуто, оцінюється раніше finally, навіть якщо це повернеться після.

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

Вихід:

X
finally trumps return... sort of
0

80
2017-12-03 23:36



Важливо знати. - Aminadav Glickshtein


Це ідея про остаточний блок. Це дозволяє переконатися, що ви виконуєте прибирання, які в іншому випадку можуть бути пропущені, оскільки ви, звичайно ж, повертаєтесь.

Нарешті називається незалежно від того, що станеться в блоці спробувати (якщо не ти дзвониш System.exit(int) або віртуальна машина Java виконується з якихось інших причин).


46
2018-05-13 06:19





Логічним способом думати про це є:

  1. Код, розміщений на кінцевому блоці, повинен бути виконаний що відбувається в межах блоку спробувати
  2. Отже, якщо код у блоці спробу намагається повернути значення або виключити виняток, елемент розміщується "на полиці", поки остаточний блок не може виконати
  3. Оскільки код у кінцевому блоці (за визначенням) має високий пріоритет, він може повернути або кинути що завгодно. У цьому випадку все, що залишилося "на полиці", відкидається.
  4. Єдине виключення з цього полягає в тому, що VM повністю вимикається під час блокування спроб, наприклад, за допомогою системи "system.exit"

31
2017-09-15 19:26



Чи це просто "логічний спосіб думати про це", чи це насправді, як остаточний блок призначений для роботи відповідно до специфікацій? Посилання на ресурс Sun буде дуже цікавим тут. - matias
Пункт 4 суперечить whatever happens твердження в пункті 1 - ojonugwa ochalifu


Також повернення нарешті викине будь-яке виняток. http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


14
2017-09-15 19:26





нарешті, завжди виконується, якщо немає ненормального завершення програми (наприклад, виклик System.exit (0) ..). так що ваш сизоут буде надруковано


13
2017-09-15 17:46