Понимание try catch finally с return и значением, которое он возвращает

у меня есть следующий фрагмент кода.

public static void main(String[] args) {
    System.out.println(returnString());
}
private static String returnString(){
    try {
        System.out.println("Executing try");
        return "Return try value";
    } catch (Exception e){
        System.out.println("Executing Catch");
        return "Return catch value";
    } finally {
        System.out.println("Executing finally");
        return "Return finally value";
    }
}

выход для этого

Executing try
Executing finally
Return finally value

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

public static void main(String[] args) {
    System.out.println(returnString());
}
private static String returnString(){
    try {
        System.out.println("Executing try");
        return "Return try value";
    } catch (Exception e){
        System.out.println("Executing Catch");
        return "Return catch value";
    } finally {
        System.out.println("Executing finally");
    }
}

тогда выход

Executing try
Executing finally
Return try value

теперь я понимаю, что, наконец, всегда выполняется, за исключением случаев вызова системы.выход (0); вызывается или JVM аварийно завершает работу. Что я не могу понять, так это почему возвращаемое значение изменилось ? Я все равно ожидаю, что он вернет значение try блок.
может ли кто-нибудь объяснить, почему значение finally учитывается, а не возвращаемое значение из блока try ?

пожалуйста, воздержитесь от ответа, потому что, наконец, выполняется, даже если есть возврат в блоке try ... или, наконец, не выполняется, только если есть система.выход (0); вызывается или JVM аварийно завершает работу. насколько я знаю.

EDIT:

(согласно Кортик комментировать этой)
public static void main(String[] args) {
    System.out.println(returnString());
}
private static String returnString(){
    try {
        System.out.println("Executing try");
        return printString("Return try value");
    } catch (Exception e){
        System.out.println("Executing Catch");
        return printString("Return catch value");
    } finally {
        System.out.println("Executing finally");
        return printString("Return finally value");
    }
}

private static String printString(String str){
    System.out.println(str);
    return str;
}

выход:

Executing try
Return try value
Executing finally
Return finally value
Return finally value

3 ответов


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

  1. в JVM встречает return оператор в основном блоке. Он приостанавливает выполнение основного блока и проверяет finally предложения.
  2. выполняет finally предложение в полном объеме, включая его return заявление.
  3. он никогда таким образом не получает, чтобы завершить try блок.

обратите внимание, однако, что try блока return выражение оценивается а потом выбрасывают. Это важно, если у него есть побочные эффекты. Так что если ваш main блок return i++ тогда это не повлияет на возвращаемое значение, но i по-прежнему будет увеличиваться. (Спасибо Кортик за указание на это.)


если у вас есть вернуться в конце концов, это окончательное возвращение.

это неудивительно. Это реальное поведение. Возвращая решил по finally заблокировать.

Если вы ничего не возвращаете в конце концов, то Предыдущее значение будет возвращаемым значением - возвращаемое значение (в вашем случае значение блока try).

независимо от того, что вы делаете в try блок finally всегда выполняется, даже если вы возврат из блока try (если у вас есть return in finally, это окончательное возвращение).

С finally docs

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

Примечание: наконец, предназначен для очистки.


в Java, код:

try {
  if (foo()) return 1;
} catch (Exception e){
  if (goo()) return 2;
} finally {
  if (moo()) return 3;
}

будет переписан компилятором в:

try {
  if (foo())
  {
    if (moo()) return 3;  // Finally code executed before return
    return 1;
  }
} catch (Exception e){
  if (goo())
  {
    if (moo()) return 3;  // Finally code executed before return
    return 2;
  }
} catch (Throwable e){
  if (moo()) return 3;   // Finally code executed before re-throw
  throw e;
}
if (moo()) return 3;    // Finally code executed before leaving block

в принципе, компилятор будет дублировать код в тег finally блок точно один раз в каждом пути выполнения, который заставит выполнение кода покинуть охраняемый блок, будь то через return, throw, или проваливается. Обратите внимание, что в то время как некоторые языки запрещают return внутри a finally блок, Java не делает; если finally блок выполнен в виде следствием исключения, однако, a return внутри блока может привести к тому, что исключение будет молча оставлено (посмотрите на код выше с пометкой "наконец-то код выполнен перед повторным броском"; если return 3; выполняется, повторный бросок будет пропущен).