Возвращаемое значение Java в режиме try-catch-finally

Я только что столкнулся с этим следующим кодом:

public class TestFinally {
    public static void main(String[] args) {
        int returnValue = function();

        System.out.println("Return value: " + returnValue);
    }

    public static int function() {
        try {
            return 1;
        } catch (Exception e){
            return 2;
        } finally{
            return 3;
        }
    }
}

Несомненно, что запуск этого кода даст выход "Возвращаемое значение: 3".

Однако мне любопытно, что:

  • Механизм внутренних функций в JVM. Кто-нибудь знает, действительно ли виртуальная машина заменяет возвращаемое значение в стеке, переписывая первый "возврат 1"? Если да, где я могу найти дополнительную информацию об этом.
  • Мне еще предстоит найти использование для возврата в механизм finally, который используется таким образом и разрешен в реализованном в JVM. Если эта конструкция кода используется как средство возврата код ошибки, это, на мой взгляд, есть лучшие способы регистрации ошибок или вернуть эти коды ошибок. Кто-нибудь нашел применение для такого построить?

Большое спасибо заранее.

Cheers, Верн

Ответ 1

То, что я нашел в спецификации языка Java, по крайней мере определяет, что ваш фрагмент кода должен возвращать 3. Конечно, в нем не упоминается, как JVM должна реализовать это, и какие возможные оптимизации можно сделать.

Раздел 14.20.2 определяет, что

Если выполнение блока try завершается внезапно по какой-либо другой причине R, выполняется блок finally. Тогда есть выбор:

  1. Если блок finally завершается нормально, то оператор try завершается преждевременно по причине R.
  2. Если блок finally завершается преждевременно по причине S, то оператор try завершается преждевременно по причине S (и причина R отбрасывается).

И начало главы 14 (точнее,раздела 14.1) указывает, что такое нормальное и резкое завершение. Например, return с заданным значением является неожиданным завершением.

Следовательно, в этом случае блок finally завершается преждевременно (причина: return с заданным значением), поэтому try завершается внезапно по той же причине (и возвращает 3). Это подтверждается также в разделе 14.17 о выражении возврата as well

Если вычисление Выражения завершается нормально, производя значение V, тогда оператор return завершается преждевременно, причиной является возврат со значением V.

Ответ 2

FWIW, я получаю предупреждение о функции:

public static int function(){
    try{
        return 1;
    }catch(Exception e){
        return 2;
    }finally{
        return 3; //WARNING on this line
    }
}

Т.е. он говорит мне, что "наконец-то блок не заканчивается нормально". Я все еще получаю 3 как возвращенную стоимость независимо от того, что.

В любом случае, если я попробую этот другой пример:

public class TestFinally {
    public static void main(String[] args) {
        int returnValue = function();

        System.out.println("Return value: " + returnValue);
    }

    public static int function() {
        try {  

            return 1;  
            }  
        catch (Exception e) {   
            return 2;  
            }  
        finally {  
            System.out.println("i don't know if this will get printed out.");
        }
    }
}

вывод будет (очевидно)

i don't know if this will get printed out.
Return value: 1

Я понятия не имею, как JVM реализует его, но самый простой способ взглянуть на него (по крайней мере концептуально):

  • возвращаемое значение в "try" помещается в стек,
  • тогда выполняется блок "finally",
  • новое возвращаемое значение помещается в стек
  • функция завершается, и возвращаемое значение выносится из стека, игнорируя первый.

Очень аккуратный вопрос.

Ответ 3

Реализация зависит от JVM, и существует много JVM. Вы можете вставить исходный код OpenJDK, чтобы увидеть, как он реализует finally, но это не единственный способ сделать это. Что касается языка, важным является поведение.

Я не понимаю пункт 2 - почему существует finally? это не так, как вы предлагаете как-то просто средство для возврата кода ошибки. Вам не нужно возвращаться из finally вообще. Конструкция существует для обеспечения того, чтобы какой-то код очистки запускался после некоторого раздела кода независимо от того, как он завершается, как обычно, так и с помощью исключения или возврата.

Ответ 4

Полностью объяснено Страница: 439 = > http://docs.oracle.com/javase/specs/jls/se8/jls8.pdf

Если оценка Expression завершается нормально, значение V, то оператор возврата завершается внезапно, причина, являющаяся возвратом со значением V.

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