Несколько возвратов: какой из них устанавливает окончательное возвращаемое значение? - PullRequest
46 голосов
/ 22 февраля 2010

Учитывая этот код:

String test() {
    try {
        return "1";
    } finally {
        return "2";
    }
}

Определяют ли языковые спецификации возвращаемое значение вызова для test()? Другими словами: всегда ли это одинаково в каждой JVM?

В Sun JVM возвращаемое значение равно 2, но я хочу быть уверен, что это не зависит от ВМ.

Ответы [ 7 ]

47 голосов
/ 22 февраля 2010

Да, спецификация языка определяет, что "2" является результатом. Если виртуальная машина делает это по-другому, это не соответствует спецификации.

Большинство компиляторов будут жаловаться на это. Eclipse, например, будет утверждать, что возвращаемый блок никогда не будет выполнен, но это неправильно.

Писать такой код - шокирующе плохая практика, никогда не делайте этого:)

20 голосов
/ 22 февраля 2010

Да, Спецификация языка Java очень четко говорит по этому вопросу ( 14.20.2 ):

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

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

Блок finally всегда будет выполняться, за исключением следующего примера:

String test() {
    try {
        System.exit(0);
    } finally {
        return "2";
    }
}

В этом случае JVM остановится без выполнения блока finally.

Итак, в вашем примере возвращаемое значение будет 2.

7 голосов
/ 22 февраля 2010

Да, если вы вернете что-то из блока finally, оно заменит все, что вы могли вернуть из блока try или catch.

То же самое верно и для исключений . Если вы выбросите что-нибудь в блок finally, это исключение заменит любое исключение, которое было сгенерировано в блоке try или catch. Поэтому будьте осторожны, чтобы никогда не бросать что-либо в блок finally, поскольку это может скрыть первоначальную причину сбоя.

3 голосов
/ 28 июля 2015

После прочтения ByteCode программы код выглядит следующим образом:

Операторы блока finally встроены перед оператором возврата блока try, поэтому возврат из блока finally выполняется первым, а исходный оператор возврата никогда не выполняется.

Для программы:

String test() {
        try {
            System.out.println("try");
            return "1";
        } finally {
            System.out.println("finally");
            return "2";
        }
    }

Преобразуется в:

String test()
    {
        System.out.println("try");
        String s = "1"; //temporary variable 
        System.out.println("finally");
        return "2";
        Exception exception;
        exception;
        System.out.println("finally");
        return "2";
    }

И Для программы: с блоком перехвата:

String test() {

        try {
            System.out.println("try");
            return "1";
        } catch (RuntimeException e) {
            System.out.println("catch");
            return "2";
        } finally {
            System.out.println("finally");
            return "3";
        }
    }

Преобразует в:

String test()
    {
        System.out.println("try");
        String s = "1";
        System.out.println("finally");
        return "3";
        RuntimeException e;
        e;
        System.out.println("catch");
        String s1 = "2";
        System.out.println("finally");
        return "3";
        Exception exception;
        exception;
        System.out.println("finally");
        return "3";
    }

Примечание: выполняется с использованием JDK 1.7 и декомпилируется с использованием Cavaj.

0 голосов
/ 14 января 2019

Над ответами очень хорошо суммированы, просто хочу добавить еще один момент здесь.

private static int divide(int a , int b){
    try{
        return  a/b;
    }finally{
        System.out.println("In finally");
        return 1;
    }
}

Если мы передадим 1,0, то вышеупомянутый метод подавит вызванное им исключение и просто вернет результат. Сказано, что приведенный выше код не должен использоваться в производственной среде.

  private static int divide(int a , int b){
        int result = 0;
        try{
            result =   a/b;
        }finally{
            System.out.println("In finally");
            result =  1;
        }

        return result;
    }

использование оператора return после блока finally приведет к

Exception in thread "main" java.lang.ArithmeticException: / by zero
0 голосов
/ 26 марта 2015

Вы можете обратиться по ссылке ниже. Я надеюсь, что это предоставит все детали:

http://www.programmerinterview.com/index.php/java-questions/will-finally-run-after-return/

В нем говорится, что блок finally всегда будет выполняться, даже если в блоке try или catch есть оператор return. И если в блоке finally также есть оператор return, то он переопределит оператор return, который находится внутри блока try или catch, и в этом случае любое исключение, выданное в try / catch, будет отброшено (плохой подход).

...