Всегда ли выполняется блок finally в Java? - PullRequest
2172 голосов
/ 15 сентября 2008

Учитывая этот код, могу ли я быть абсолютно уверенным , что блок 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");
}

Ответы [ 46 ]

2434 голосов
/ 15 сентября 2008

Да, finally будет вызываться после выполнения кодовых блоков try или catch.

Единственные случаи, когда finally не будут вызваны:

  1. Если вы вызываете System.exit()
  2. Если JVM аварийно завершает работу
  3. Если JVM достигает бесконечного цикла (или некоторого другого не прерываемого, не завершающего оператора) в блоке try или catch
  4. Если ОС принудительно завершает процесс JVM; например, kill -9 <pid> в UNIX
  5. Если хост-система умирает; например, сбой питания, аппаратная ошибка, паника ОС и т. д.
  6. Если блок finally будет выполняться потоком демона, а все остальные потоки, не являющиеся демонами, завершатся до того, как finally будет вызван
507 голосов
/ 15 сентября 2008

Пример кода:

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
363 голосов
/ 15 сентября 2008

Кроме того, хотя это плохая практика, если в блоке finally есть оператор return, он превзойдет любой другой возврат из обычного блока. То есть следующий блок вернул бы false:

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

То же самое с генерацией исключений из блока finally.

249 голосов
/ 25 мая 2010

Вот официальные слова из спецификации языка Java.

14.20.2. Выполнение try-finally и try-catch-finally

Оператор try с блоком finally выполняется первым выполнением блока try. Тогда есть выбор:

  • Если выполнение блока try завершается нормально, [...]
  • Если выполнение блока try завершается внезапно из-за throw значения V , [...]
  • Если выполнение блока try завершается внезапно по любой другой причине R , то выполняется блок finally. Тогда есть выбор:
    • Если блок finally завершается нормально, то оператор try завершается внезапно по причине R .
    • Если блок finally завершается преждевременно по причине S , то оператор try завершается преждевременно по причине S ( и причине R * 1046) * отбрасывается ).

Спецификация для return фактически делает это явным:

JLS 14.17 Заявление о возврате

ReturnStatement:
     return Expression(opt) ;

Оператор return без Expression пытается передать управление вызывающему методу или конструктору, который его содержит.

оператор return с Expression пытается передать управление вызывающему методу, который его содержит; значение Expression становится значением вызова метода.

В предыдущих описаниях говорится " пытается передать управление ", а не просто " передает управление ", потому что если в методе есть какие-либо операторы try или конструктор, чьи блоки try содержат оператор return, тогда любые операторы finally этих операторов try будут выполняться по порядку, от самого внутреннего к внешнему, до того, как управление передается вызывающему методу или конструктору. Внезапное завершение предложения finally может нарушить передачу управления, инициированную оператором return.

148 голосов
/ 13 мая 2010

В дополнение к другим ответам важно указать, что 'finally' имеет право переопределить любое исключение / возвращаемое значение блоком 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();
    }
}
109 голосов
/ 17 ноября 2008

Я попробовал приведенный выше пример с небольшой модификацией -

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 out.

После выполнения блока finally блок try возвращает 2, а не 12, поскольку этот оператор возврата больше не выполняется.

Если вы будете отлаживать этот код в Eclipse, у вас будет ощущение, что после выполнения System.out из finally блока оператор return из блока try выполняется снова. Но это не так. Он просто возвращает значение 2.

96 голосов
/ 04 декабря 2013

Вот уточнение Кевина . Важно знать, что возвращаемое выражение вычисляется до 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
52 голосов
/ 13 мая 2010

Вот и вся идея окончательного блока. Это позволяет вам убедиться, что вы делаете очистки, которые в противном случае могли бы быть пропущены, потому что вы возвращаетесь, среди прочего, конечно.

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

38 голосов
/ 15 сентября 2008

Логичный способ думать об этом:

  1. Код, помещенный в блок finally, должен выполняться , что бы ни происходило в блоке try
  2. Таким образом, если код в блоке try пытается вернуть значение или сгенерировать исключение, элемент помещается «на полку», пока блок finally не сможет выполнить
  3. Поскольку код в блоке finally имеет (по определению) высокий приоритет, он может возвращать или выдавать все, что пожелает. В этом случае все, что осталось «на полке», отбрасывается.
  4. Единственное исключение из этого, если виртуальная машина полностью отключается во время блока try, например от 'System.exit'
18 голосов
/ 15 сентября 2008

finally всегда выполняется, если не происходит аварийного завершения программы (например, вызов System.exit (0) ..). Итак, ваш sysout будет напечатан

...